From d9357f1e32ba66980ee78afc7169478197537f8d Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Tue, 21 Jul 2020 15:12:37 -0700 Subject: [PATCH] For #12801 - Extra debug menu trigger, disable if already triggered --- .../fenix/settings/about/AboutFragment.kt | 46 +------ .../settings/about/SecretDebugMenuTrigger.kt | 71 +++++++++++ .../about/SecretDebugMenuTriggerTest.kt | 114 ++++++++++++++++++ 3 files changed, 190 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/about/SecretDebugMenuTrigger.kt create mode 100644 app/src/test/java/org/mozilla/fenix/settings/about/SecretDebugMenuTriggerTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt index 363bf901c..7bda04ec7 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -10,13 +10,11 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.core.content.pm.PackageInfoCompat import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DividerItemDecoration import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import kotlinx.android.synthetic.main.fragment_about.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.HomeActivity @@ -39,10 +37,9 @@ import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig * Displays the logo and information about the app, including library versions. */ class AboutFragment : Fragment(), AboutPageListener { + private lateinit var appName: String private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) - private var secretDebugMenuClicks = 0 - private var lastDebugMenuToast: Toast? = null override fun onCreateView( inflater: LayoutInflater, @@ -56,15 +53,7 @@ class AboutFragment : Fragment(), AboutPageListener { return rootView } - override fun onResume() { - super.onResume() - secretDebugMenuClicks = 0 - } - - @ExperimentalCoroutinesApi override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - about_list.run { adapter = aboutPageAdapter addItemDecoration( @@ -75,33 +64,10 @@ class AboutFragment : Fragment(), AboutPageListener { ) } - // 5 taps on the logo activate the "secret" debug menu. - wordmark.setOnClickListener { - // Because the user will mostly likely tap the logo in rapid succession, - // we ensure only 1 toast is shown at any given time. - lastDebugMenuToast?.let { toast -> toast.cancel() } - secretDebugMenuClicks += 1 - when (secretDebugMenuClicks) { - in 2 until SECRET_DEBUG_MENU_CLICKS -> { - val clicksLeft = SECRET_DEBUG_MENU_CLICKS - secretDebugMenuClicks - val toast = Toast.makeText( - context, - getString(R.string.about_debug_menu_toast_progress, clicksLeft), - Toast.LENGTH_SHORT - ) - toast.show() - lastDebugMenuToast = toast - } - SECRET_DEBUG_MENU_CLICKS -> { - Toast.makeText( - context, - getString(R.string.about_debug_menu_toast_done), - Toast.LENGTH_LONG - ).show() - requireContext().settings().showSecretDebugMenuThisSession = true - } - } - } + lifecycle.addObserver(SecretDebugMenuTrigger( + logoView = wordmark, + settings = view.context.settings() + )) populateAboutHeader() aboutPageAdapter.submitList(populateAboutList()) @@ -233,7 +199,5 @@ class AboutFragment : Fragment(), AboutPageListener { companion object { private const val ABOUT_LICENSE_URL = "about:license" - // Number of clicks on the app logo to enable the "secret" debug menu. - private const val SECRET_DEBUG_MENU_CLICKS = 5 } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/SecretDebugMenuTrigger.kt b/app/src/main/java/org/mozilla/fenix/settings/about/SecretDebugMenuTrigger.kt new file mode 100644 index 000000000..75f42943b --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/SecretDebugMenuTrigger.kt @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.view.View +import android.widget.Toast +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent +import org.mozilla.fenix.R +import org.mozilla.fenix.utils.Settings + +/** + * Triggers the "secret" debug menu when logoView is tapped 5 times. + */ +class SecretDebugMenuTrigger( + logoView: View, + private val settings: Settings +) : View.OnClickListener, LifecycleObserver { + + private var secretDebugMenuClicks = 0 + private var lastDebugMenuToast: Toast? = null + + init { + if (!settings.showSecretDebugMenuThisSession) { + logoView.setOnClickListener(this) + } + } + + /** + * Reset the [secretDebugMenuClicks] counter. + */ + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun clearClickCounter() { + secretDebugMenuClicks = 0 + } + + override fun onClick(v: View) { + // Because the user will mostly likely tap the logo in rapid succession, + // we ensure only 1 toast is shown at any given time. + lastDebugMenuToast?.cancel() + secretDebugMenuClicks += 1 + when (secretDebugMenuClicks) { + in 2 until SECRET_DEBUG_MENU_CLICKS -> { + val clicksLeft = SECRET_DEBUG_MENU_CLICKS - secretDebugMenuClicks + val toast = Toast.makeText( + v.context, + v.context.getString(R.string.about_debug_menu_toast_progress, clicksLeft), + Toast.LENGTH_SHORT + ) + toast.show() + lastDebugMenuToast = toast + } + SECRET_DEBUG_MENU_CLICKS -> { + Toast.makeText( + v.context, + R.string.about_debug_menu_toast_done, + Toast.LENGTH_LONG + ).show() + settings.showSecretDebugMenuThisSession = true + } + } + } + + companion object { + // Number of clicks on the app logo to enable the "secret" debug menu. + private const val SECRET_DEBUG_MENU_CLICKS = 5 + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/about/SecretDebugMenuTriggerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/about/SecretDebugMenuTriggerTest.kt new file mode 100644 index 000000000..5440ddba8 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/about/SecretDebugMenuTriggerTest.kt @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.content.Context +import android.view.View +import android.widget.Toast +import io.mockk.CapturingSlot +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkStatic +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mozilla.fenix.R +import org.mozilla.fenix.utils.Settings + +class SecretDebugMenuTriggerTest { + + @MockK private lateinit var logoView: View + @MockK private lateinit var context: Context + @MockK private lateinit var settings: Settings + @MockK(relaxUnitFun = true) private lateinit var toast: Toast + private lateinit var clickListener: CapturingSlot + + @Before + fun setup() { + MockKAnnotations.init(this) + mockkStatic(Toast::class) + clickListener = slot() + + every { logoView.setOnClickListener(capture(clickListener)) } just Runs + every { logoView.context } returns context + every { + context.getString(R.string.about_debug_menu_toast_progress, any()) + } returns "Debug menu: x click(s) left to enable" + every { settings.showSecretDebugMenuThisSession } returns false + every { settings.showSecretDebugMenuThisSession = any() } just Runs + every { Toast.makeText(context, any(), any()) } returns toast + every { Toast.makeText(context, any(), any()) } returns toast + } + + @After + fun teardown() { + unmockkStatic(Toast::class) + } + + @Test + fun `toast is not displayed on first click`() { + SecretDebugMenuTrigger(logoView, settings) + clickListener.captured.onClick(logoView) + + verify(inverse = true) { Toast.makeText(context, any(), any()) } + verify(inverse = true) { toast.show() } + } + + @Test + fun `toast is displayed on second click`() { + SecretDebugMenuTrigger(logoView, settings) + clickListener.captured.onClick(logoView) + clickListener.captured.onClick(logoView) + + verify { context.getString(R.string.about_debug_menu_toast_progress, 3) } + verify { Toast.makeText(context, any(), Toast.LENGTH_SHORT) } + verify { toast.show() } + } + + @Test + fun `clearClickCounter resets counter`() { + val trigger = SecretDebugMenuTrigger(logoView, settings) + + clickListener.captured.onClick(logoView) + trigger.clearClickCounter() + + clickListener.captured.onClick(logoView) + + verify(inverse = true) { Toast.makeText(context, any(), any()) } + verify(inverse = true) { toast.show() } + } + + @Test + fun `toast is displayed on fifth click`() { + SecretDebugMenuTrigger(logoView, settings) + clickListener.captured.onClick(logoView) + clickListener.captured.onClick(logoView) + clickListener.captured.onClick(logoView) + clickListener.captured.onClick(logoView) + clickListener.captured.onClick(logoView) + + verify { Toast.makeText( + context, + R.string.about_debug_menu_toast_done, + Toast.LENGTH_LONG + ) } + verify { toast.show() } + verify { settings.showSecretDebugMenuThisSession = true } + } + + @Test + fun `don't register click listener if menu is already shown`() { + every { settings.showSecretDebugMenuThisSession } returns true + SecretDebugMenuTrigger(logoView, settings) + + verify(inverse = true) { logoView.setOnClickListener(any()) } + } +}