From 8596e6ffdfbf85750afb333f9264a45db0c30ecc Mon Sep 17 00:00:00 2001 From: Niko Diamadis Date: Tue, 3 Oct 2023 11:12:33 +0200 Subject: [PATCH 1/2] Prevent direct execution of intentURLs with confirmation dialog --- app/src/main/AndroidManifest.xml | 4 ++ .../messages/IntentUrlDialogActivity.kt | 33 ++++++++++++ .../github/gotify/service/WebSocketService.kt | 8 +-- .../res/layout/activity_dialog_intent_url.xml | 52 +++++++++++++++++++ app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/values/styles.xml | 6 +++ 6 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/com/github/gotify/messages/IntentUrlDialogActivity.kt create mode 100644 app/src/main/res/layout/activity_dialog_intent_url.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aa605e8..6cbd775 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -67,6 +67,10 @@ + diff --git a/app/src/main/kotlin/com/github/gotify/messages/IntentUrlDialogActivity.kt b/app/src/main/kotlin/com/github/gotify/messages/IntentUrlDialogActivity.kt new file mode 100644 index 0000000..a2bebcc --- /dev/null +++ b/app/src/main/kotlin/com/github/gotify/messages/IntentUrlDialogActivity.kt @@ -0,0 +1,33 @@ +package com.github.gotify.messages + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.github.gotify.databinding.ActivityDialogIntentUrlBinding + +internal class IntentUrlDialogActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setFinishOnTouchOutside(false) + val binding = ActivityDialogIntentUrlBinding.inflate(layoutInflater) + val intentUrl = intent.getStringExtra(EXTRA_KEY_URL) + assert(intentUrl != null) { "intentUrl may not be empty" } + + binding.urlView.text = intentUrl + binding.openButton.setOnClickListener { + finish() + Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(intentUrl) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + startActivity(this) + } + } + binding.cancelButton.setOnClickListener { finish() } + setContentView(binding.root) + } + + companion object { + const val EXTRA_KEY_URL = "url" + } +} diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt index 69401df..9b6d981 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt @@ -31,6 +31,7 @@ import com.github.gotify.client.model.Message import com.github.gotify.log.Log import com.github.gotify.log.UncaughtExceptionHandler import com.github.gotify.messages.Extras +import com.github.gotify.messages.IntentUrlDialogActivity import com.github.gotify.messages.MessagesActivity import com.github.gotify.picasso.PicassoHandler import io.noties.markwon.Markwon @@ -320,9 +321,10 @@ internal class WebSocketService : Service() { ) if (intentUrl != null) { - intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(intentUrl) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent = Intent(this, IntentUrlDialogActivity::class.java).apply { + putExtra(IntentUrlDialogActivity.EXTRA_KEY_URL, intentUrl) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } startActivity(intent) } diff --git a/app/src/main/res/layout/activity_dialog_intent_url.xml b/app/src/main/res/layout/activity_dialog_intent_url.xml new file mode 100644 index 0000000..3990852 --- /dev/null +++ b/app/src/main/res/layout/activity_dialog_intent_url.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49a81a7..478b4d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -96,6 +96,10 @@ There are no applications available on the server to push a message to. Content copied to clipboard Cannot share to Gotify, because you aren\'t logged in. + Missing URL + You have received a message with an intent url: + Open + Cancel Not connected Trying to reconnect diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 84d85d5..c426eaa 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -39,6 +39,12 @@ + From 45b41b5dd3158a60d17bdffd78ee48a9a9752d0d Mon Sep 17 00:00:00 2001 From: Niko Diamadis Date: Tue, 3 Oct 2023 16:29:46 +0200 Subject: [PATCH 2/2] Add preference for giving SYSTEM_ALERT_WINDOW permission --- .../gotify/settings/SettingsActivity.kt | 39 +++++++++++++++++++ app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/xml/root_preferences.xml | 8 +++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt b/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt index 9f4ef92..26c4516 100644 --- a/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt @@ -5,8 +5,10 @@ import android.content.DialogInterface import android.content.Intent import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.net.Uri import android.os.Build import android.os.Bundle +import android.provider.Settings import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity @@ -97,6 +99,14 @@ internal class SettingsActivity : AppCompatActivity(), OnSharedPreferenceChangeL Utils.setExcludeFromRecent(requireContext(), value as Boolean) return@OnPreferenceChangeListener true } + findPreference( + getString(R.string.setting_key_intent_dialog_permission) + )?.let { + it.setOnPreferenceChangeListener { _, _ -> + openSystemAlertWindowPermissionPage() + } + } + checkSystemAlertWindowPermission() } override fun onDisplayPreferenceDialog(preference: Preference) { @@ -107,6 +117,35 @@ internal class SettingsActivity : AppCompatActivity(), OnSharedPreferenceChangeL } } + override fun onResume() { + super.onResume() + checkSystemAlertWindowPermission() + } + + private fun openSystemAlertWindowPermissionPage(): Boolean { + Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:${requireContext().packageName}") + ).apply { + startActivity(this) + } + return true + } + + private fun checkSystemAlertWindowPermission() { + findPreference( + getString(R.string.setting_key_intent_dialog_permission) + )?.let { + val canDrawOverlays = Settings.canDrawOverlays(requireContext()) + it.isChecked = canDrawOverlays + it.summary = if (canDrawOverlays) { + getString(R.string.setting_summary_intent_dialog_permission_granted) + } else { + getString(R.string.setting_summary_intent_dialog_permission) + } + } + } + private fun showListPreferenceDialog(preference: ListPreference) { val dialogFragment = MaterialListPreference() dialogFragment.arguments = Bundle(1).apply { putString("key", preference.key) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 478b4d3..b79f30e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -86,6 +86,10 @@ notification_channels exclude_from_recent Exclude from recents + Intent Action Permission + intent_dialog_permission + To always show incoming intent URLs, give permission to show this app on top of other apps. + Permission granted. Push message App: Priority: diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 2373083..d6d4ad0 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -1,5 +1,6 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + +