From 143aaece4b97114bc0627f3f7f6779afff4b0d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 30 Jun 2023 09:29:36 +0200 Subject: [PATCH 1/5] Add password protection feature to the app This closes #446 --- app/src/main/AndroidManifest.xml | 6 ++--- .../smsmessenger/activities/MainActivity.kt | 9 +++++++ .../activities/SettingsActivity.kt | 27 ++++++++++++++++--- app/src/main/res/layout/activity_settings.xml | 26 ++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 91dfc6ec..3820008d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ @@ -55,9 +55,9 @@ android:name=".activities.ThreadActivity" android:configChanges="orientation" android:exported="false" + android:launchMode="singleTop" android:parentActivityName=".activities.MainActivity" - android:windowSoftInputMode="adjustResize" - android:launchMode="singleTop" /> + android:windowSoftInputMode="adjustResize" /> toast(R.string.invalid_file_format) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index fc86ece8..f366044f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -5,9 +5,7 @@ import android.content.Intent import android.os.Build import android.os.Bundle import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity -import com.simplemobiletools.commons.dialogs.ChangeDateTimeFormatDialog -import com.simplemobiletools.commons.dialogs.FeatureLockedDialog -import com.simplemobiletools.commons.dialogs.RadioGroupDialog +import com.simplemobiletools.commons.dialogs.* import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.RadioItem @@ -49,6 +47,7 @@ class SettingsActivity : SimpleActivity() { setupGroupMessageAsMMS() setupLockScreenVisibility() setupMMSFileSizeLimit() + setupAppPasswordProtection() updateTextColors(settings_nested_scrollview) if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) { @@ -244,6 +243,28 @@ class SettingsActivity : SimpleActivity() { } } + private fun setupAppPasswordProtection() { + settings_app_password_protection.isChecked = config.isAppPasswordProtectionOn + settings_app_password_protection_holder.setOnClickListener { + val tabToShow = if (config.isAppPasswordProtectionOn) config.appProtectionType else SHOW_ALL_TABS + SecurityDialog(this, config.appPasswordHash, tabToShow) { hash, type, success -> + if (success) { + val hasPasswordProtection = config.isAppPasswordProtectionOn + settings_app_password_protection.isChecked = !hasPasswordProtection + config.isAppPasswordProtectionOn = !hasPasswordProtection + config.appPasswordHash = if (hasPasswordProtection) "" else hash + config.appProtectionType = type + + if (config.isAppPasswordProtectionOn) { + val confirmationTextId = if (config.appProtectionType == PROTECTION_FINGERPRINT) + R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully + ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { } + } + } + } + } + } + private fun getMMSFileLimitText() = getString( when (config.mmsFileSizeLimit) { FILE_SIZE_100_KB -> R.string.mms_file_size_limit_100kb diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 7083d0c3..99dfaa9b 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -341,6 +341,32 @@ tools:text="@string/mms_file_size_limit_none" /> + + + + + + + + + + From ce76573614fee4b063f011a4a6ae087ad4fe6060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 30 Jun 2023 10:25:45 +0200 Subject: [PATCH 2/5] Adjust settings section label color properly --- .../smsmessenger/activities/SettingsActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index f366044f..d97e67d0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -58,7 +58,8 @@ class SettingsActivity : SimpleActivity() { settings_color_customization_section_label, settings_general_settings_label, settings_outgoing_messages_label, - settings_notifications_label + settings_notifications_label, + settings_security_label ).forEach { it.setTextColor(getProperPrimaryColor()) } From ae2e480876b91c3fb014543d1ed7da65396676d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 30 Jun 2023 10:41:01 +0200 Subject: [PATCH 3/5] Prevent loading messages before app is unlocked --- .../smsmessenger/activities/MainActivity.kt | 75 +++++++++++++------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt index 35222eb0..5413eda7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -50,6 +50,7 @@ class MainActivity : SimpleActivity() { private var lastSearchedText = "" private var bus: EventBus? = null private val smsExporter by lazy { MessagesExporter(this) } + private var wasProtectionHandled = false @SuppressLint("InlinedApi") override fun onCreate(savedInstanceState: Bundle?) { @@ -64,7 +65,10 @@ class MainActivity : SimpleActivity() { if (savedInstanceState == null) { handleAppPasswordProtection { - if (!it) { + wasProtectionHandled = it + if (it) { + loadMessages() + } else { finish() } } @@ -74,29 +78,6 @@ class MainActivity : SimpleActivity() { return } - if (isQPlus()) { - val roleManager = getSystemService(RoleManager::class.java) - if (roleManager!!.isRoleAvailable(RoleManager.ROLE_SMS)) { - if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) { - askPermissions() - } else { - val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS) - startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST) - } - } else { - toast(R.string.unknown_error_occurred) - finish() - } - } else { - if (Telephony.Sms.getDefaultSmsPackage(this) == packageName) { - askPermissions() - } else { - val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT) - intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName) - startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST) - } - } - clearAllMessagesIfNeeded() } @@ -148,6 +129,27 @@ class MainActivity : SimpleActivity() { } } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(WAS_PROTECTION_HANDLED, wasProtectionHandled) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) { + super.onRestoreInstanceState(savedInstanceState) + wasProtectionHandled = savedInstanceState.getBoolean(WAS_PROTECTION_HANDLED, false) + + if (!wasProtectionHandled) { + handleAppPasswordProtection { + wasProtectionHandled = it + if (it) { + loadMessages() + } else { + finish() + } + } + } + } + private fun setupOptionsMenu() { main_menu.getToolbar().inflateMenu(R.menu.menu_main) main_menu.toggleHideOnScroll(true) @@ -213,6 +215,31 @@ class MainActivity : SimpleActivity() { main_menu.updateColors() } + private fun loadMessages() { + if (isQPlus()) { + val roleManager = getSystemService(RoleManager::class.java) + if (roleManager!!.isRoleAvailable(RoleManager.ROLE_SMS)) { + if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) { + askPermissions() + } else { + val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS) + startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST) + } + } else { + toast(R.string.unknown_error_occurred) + finish() + } + } else { + if (Telephony.Sms.getDefaultSmsPackage(this) == packageName) { + askPermissions() + } else { + val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT) + intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName) + startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST) + } + } + } + // while SEND_SMS and READ_SMS permissions are mandatory, READ_CONTACTS is optional. If we don't have it, we just won't be able to show the contact name in some cases private fun askPermissions() { handlePermission(PERMISSION_READ_SMS) { From 8675de70c29fcedeae4e2a973d0af2bd0e0b19c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 30 Jun 2023 10:55:30 +0200 Subject: [PATCH 4/5] Handle app locking for threads too --- .../smsmessenger/activities/MainActivity.kt | 1 + .../smsmessenger/activities/ThreadActivity.kt | 77 +++++++++++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt index 5413eda7..863f6e73 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -445,6 +445,7 @@ class MainActivity : SimpleActivity() { val conversation = any as Conversation putExtra(THREAD_ID, conversation.threadId) putExtra(THREAD_TITLE, conversation.title) + putExtra(WAS_PROTECTION_HANDLED, wasProtectionHandled) startActivity(this) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt index 6a116d02..08f251ac 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt @@ -101,6 +101,7 @@ class ThreadActivity : SimpleActivity() { private var loadingOlderMessages = false private var allMessagesFetched = false private var oldestMessageDate = -1 + private var wasProtectionHandled = false private var isScheduledMessage: Boolean = false private var scheduledMessage: Message? = null @@ -111,7 +112,7 @@ class ThreadActivity : SimpleActivity() { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) finish() - startActivity(intent) + startActivity(intent) } override fun onCreate(savedInstanceState: Bundle?) { @@ -136,28 +137,23 @@ class ThreadActivity : SimpleActivity() { intent.getStringExtra(THREAD_TITLE)?.let { thread_toolbar.title = it } + wasProtectionHandled = intent.getBooleanExtra(WAS_PROTECTION_HANDLED, false) bus = EventBus.getDefault() bus!!.register(this) - handlePermission(PERMISSION_READ_PHONE_STATE) { granted -> - if (granted) { - setupButtons() - setupConversation() - setupCachedMessages { - val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L) - intent.removeExtra(SEARCHED_MESSAGE_ID) - if (searchedMessageId != -1L) { - val index = threadItems.indexOfFirst { (it as? Message)?.id == searchedMessageId } - if (index != -1) { - thread_messages_list.smoothScrollToPosition(index) - } - } - setupThread() - setupScrollFab() + if (savedInstanceState == null) { + if (!wasProtectionHandled) { + handleAppPasswordProtection { + wasProtectionHandled = it + if (it) { + loadConversation() + } else { + finish() + } } } else { - finish() + loadConversation() } } @@ -221,6 +217,29 @@ class ThreadActivity : SimpleActivity() { bus?.unregister(this) } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(WAS_PROTECTION_HANDLED, wasProtectionHandled) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) { + super.onRestoreInstanceState(savedInstanceState) + wasProtectionHandled = savedInstanceState.getBoolean(WAS_PROTECTION_HANDLED, false) + + if (!wasProtectionHandled) { + handleAppPasswordProtection { + wasProtectionHandled = it + if (it) { + loadConversation() + } else { + finish() + } + } + } else { + loadConversation() + } + } + private fun refreshMenuItems() { val firstPhoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.value thread_toolbar.menu.apply { @@ -546,6 +565,30 @@ class ThreadActivity : SimpleActivity() { } } + private fun loadConversation() { + handlePermission(PERMISSION_READ_PHONE_STATE) { granted -> + if (granted) { + setupButtons() + setupConversation() + setupCachedMessages { + val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L) + intent.removeExtra(SEARCHED_MESSAGE_ID) + if (searchedMessageId != -1L) { + val index = threadItems.indexOfFirst { (it as? Message)?.id == searchedMessageId } + if (index != -1) { + thread_messages_list.smoothScrollToPosition(index) + } + } + + setupThread() + setupScrollFab() + } + } else { + finish() + } + } + } + private fun setupConversation() { ensureBackgroundThread { conversation = conversationsDB.getConversationWithThreadId(threadId) From 9ff6c3cbb65ff22f84033dc7cbb9b88cbaf16ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 30 Jun 2023 10:56:07 +0200 Subject: [PATCH 5/5] Properly load threads on configuration changes --- .../simplemobiletools/smsmessenger/activities/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt index 863f6e73..1204d748 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -147,6 +147,8 @@ class MainActivity : SimpleActivity() { finish() } } + } else { + loadMessages() } }