From d66da53c9ae3161f61e11868396f951dd558715c Mon Sep 17 00:00:00 2001 From: Mihai Eduard Badea Date: Fri, 3 Apr 2020 12:19:39 +0300 Subject: [PATCH] For #2768 - Prevent screenshots in private mode Added a new option in Private browsing menu to allow or prevent screenshots from being taken while in private mode by adding or removing the FLAG_SECURE flag from the home activity's window. This method is called whenever the activity is initialized to account for the browsing mode being changed and whenever the setting from the Private browsing menu is changed. The setting is by default set to true (screenshots are allowed to be taken) --- .../java/org/mozilla/fenix/HomeActivity.kt | 2 + .../java/org/mozilla/fenix/ext/Activity.kt | 17 +++++ .../fenix/settings/PrivateBrowsingFragment.kt | 12 ++++ .../java/org/mozilla/fenix/utils/Settings.kt | 5 ++ app/src/main/res/values/preference_keys.xml | 1 + app/src/main/res/values/strings.xml | 2 + .../res/xml/private_browsing_preferences.xml | 5 ++ .../org/mozilla/fenix/ext/ActivityTest.kt | 68 ++++++++++++++++++- 8 files changed, 111 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index a5007b6f0..c326c89d7 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -48,6 +48,7 @@ import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.exceptions.ExceptionsFragmentDirections import org.mozilla.fenix.ext.alreadyOnDestination +import org.mozilla.fenix.ext.checkAndUpdateScreenshotPermission import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.settings @@ -116,6 +117,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { components.publicSuffixList.prefetch() setupThemeAndBrowsingMode(getModeFromIntentOrLastKnown(intent)) + checkAndUpdateScreenshotPermission(settings()) setContentView(R.layout.activity_home) // Must be after we set the content view diff --git a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt index 7dd2b3dce..246e77113 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.ext import android.app.Activity import android.view.View import android.view.WindowManager +import org.mozilla.fenix.utils.Settings /** * Attempts to call immersive mode using the View to hide the status bar and navigation buttons. @@ -22,3 +23,19 @@ fun Activity.enterToImmersiveMode() { or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) } + +/** + * Prevents or allows screenshots from being taken in private mode based on the user preferences. + * + * The default setting is set to true (screenshots are allowed to be taken in private mode), as + * described in #2768 + */ +fun Activity.checkAndUpdateScreenshotPermission(settings: Settings) { + if (!settings.allowScreenshotsInPrivateMode && + settings.lastKnownMode.isPrivate + ) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/PrivateBrowsingFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/PrivateBrowsingFragment.kt index 05355f6d3..ff1dc9b07 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/PrivateBrowsingFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/PrivateBrowsingFragment.kt @@ -11,8 +11,10 @@ import androidx.preference.SwitchPreference import org.mozilla.fenix.R import org.mozilla.fenix.components.PrivateShortcutCreateManager import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.checkAndUpdateScreenshotPermission import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.metrics +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar /** @@ -41,5 +43,15 @@ class PrivateBrowsingFragment : PreferenceFragmentCompat() { findPreference(getPreferenceKey(R.string.pref_key_open_links_in_a_private_tab))?.apply { onPreferenceChangeListener = SharedPreferenceUpdater() } + + findPreference(getPreferenceKey + (R.string.pref_key_allow_screenshots_in_private_mode))?.apply { + onPreferenceChangeListener = object : SharedPreferenceUpdater() { + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + return super.onPreferenceChange(preference, newValue).also { + requireActivity().checkAndUpdateScreenshotPermission(requireActivity().settings()) } + } + } + } } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 73b1de499..5b15f81e6 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -132,6 +132,11 @@ class Settings private constructor( default = false ) + var allowScreenshotsInPrivateMode by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_allow_screenshots_in_private_mode), + default = true + ) + var defaultSearchEngineName by stringPreference( appContext.getPreferenceKey(R.string.pref_key_search_engine), default = "" diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 8f7785775..c4c9b44ea 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -142,6 +142,7 @@ pref_key_open_links_in_a_private_tab pref_key_open_links_in_external_app + pref_key_allow_screenshots_in_private_mode pref_key_bounce_quick_action diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ac2b5688..5acb0047b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -203,6 +203,8 @@ Private browsing Open links in a private tab + + Allow screenshots in private browsing Add private browsing shortcut diff --git a/app/src/main/res/xml/private_browsing_preferences.xml b/app/src/main/res/xml/private_browsing_preferences.xml index 3f6be2234..6c43eefb9 100644 --- a/app/src/main/res/xml/private_browsing_preferences.xml +++ b/app/src/main/res/xml/private_browsing_preferences.xml @@ -13,4 +13,9 @@ android:key="@string/pref_key_open_links_in_a_private_tab" android:title="@string/preferences_open_links_in_a_private_tab" app:iconSpaceReserved="false" /> + diff --git a/app/src/test/java/org/mozilla/fenix/ext/ActivityTest.kt b/app/src/test/java/org/mozilla/fenix/ext/ActivityTest.kt index 5e22803e4..dfb2816a3 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/ActivityTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/ActivityTest.kt @@ -7,12 +7,15 @@ package org.mozilla.fenix.ext import android.app.Activity import android.view.View import android.view.WindowManager +import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith -import org.robolectric.Robolectric +import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf @RunWith(FenixRobolectricTestRunner::class) @@ -39,4 +42,67 @@ class ActivityTest { for (f in flags) assertEquals(f, window.decorView.systemUiVisibility and f) assertTrue(shadowOf(window).getFlag(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)) } + + @Test + fun `testCheckAndUpdateScreenshotPermission adds flag in private mode when screenshots are not allowed `() { + // given + val activity = Robolectric.buildActivity(Activity::class.java).create().get() + val window = activity.window + testContext.settings().lastKnownMode = BrowsingMode.Private + testContext.settings().allowScreenshotsInPrivateMode = false + + // when + activity.checkAndUpdateScreenshotPermission(activity.settings()) + + // then + assertTrue(shadowOf(window).getFlag(WindowManager.LayoutParams.FLAG_SECURE)) + } + + @Test + fun `testCheckAndUpdateScreenshotPermission removes flag in private mode when screenshots are allowed `() { + // given + val activity = Robolectric.buildActivity(Activity::class.java).create().get() + val window = activity.window + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + testContext.settings().lastKnownMode = BrowsingMode.Private + testContext.settings().allowScreenshotsInPrivateMode = true + + // when + activity.checkAndUpdateScreenshotPermission(activity.settings()) + + // then + assertFalse(shadowOf(window).getFlag(WindowManager.LayoutParams.FLAG_SECURE)) + } + + @Test + fun `testCheckAndUpdateScreenshotPermission removes flag in normal mode when screenshots are allowed `() { + // given + val activity = Robolectric.buildActivity(Activity::class.java).create().get() + val window = activity.window + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + testContext.settings().lastKnownMode = BrowsingMode.Normal + testContext.settings().allowScreenshotsInPrivateMode = true + + // when + activity.checkAndUpdateScreenshotPermission(activity.settings()) + + // then + assertFalse(shadowOf(window).getFlag(WindowManager.LayoutParams.FLAG_SECURE)) + } + + @Test + fun `testCheckAndUpdateScreenshotPermission removes flag when in normal mode screenshots are not allowed `() { + // given + val activity = Robolectric.buildActivity(Activity::class.java).create().get() + val window = activity.window + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + testContext.settings().lastKnownMode = BrowsingMode.Normal + testContext.settings().allowScreenshotsInPrivateMode = false + + // when + activity.checkAndUpdateScreenshotPermission(activity.settings()) + + // then + assertFalse(shadowOf(window).getFlag(WindowManager.LayoutParams.FLAG_SECURE)) + } }