1
0
Fork 0

For #12801 - Extra debug menu trigger, disable if already triggered

master
Tiger Oakes 2020-07-21 15:12:37 -07:00 committed by Mihai Branescu
parent d5fbc17ccf
commit d9357f1e32
3 changed files with 190 additions and 41 deletions

View File

@ -10,13 +10,11 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import kotlinx.android.synthetic.main.fragment_about.* import kotlinx.android.synthetic.main.fragment_about.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.HomeActivity 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. * Displays the logo and information about the app, including library versions.
*/ */
class AboutFragment : Fragment(), AboutPageListener { class AboutFragment : Fragment(), AboutPageListener {
private lateinit var appName: String private lateinit var appName: String
private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this)
private var secretDebugMenuClicks = 0
private var lastDebugMenuToast: Toast? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -56,15 +53,7 @@ class AboutFragment : Fragment(), AboutPageListener {
return rootView return rootView
} }
override fun onResume() {
super.onResume()
secretDebugMenuClicks = 0
}
@ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
about_list.run { about_list.run {
adapter = aboutPageAdapter adapter = aboutPageAdapter
addItemDecoration( addItemDecoration(
@ -75,33 +64,10 @@ class AboutFragment : Fragment(), AboutPageListener {
) )
} }
// 5 taps on the logo activate the "secret" debug menu. lifecycle.addObserver(SecretDebugMenuTrigger(
wordmark.setOnClickListener { logoView = wordmark,
// Because the user will mostly likely tap the logo in rapid succession, settings = view.context.settings()
// 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
}
}
}
populateAboutHeader() populateAboutHeader()
aboutPageAdapter.submitList(populateAboutList()) aboutPageAdapter.submitList(populateAboutList())
@ -233,7 +199,5 @@ class AboutFragment : Fragment(), AboutPageListener {
companion object { companion object {
private const val ABOUT_LICENSE_URL = "about:license" 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
} }
} }

View File

@ -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
}
}

View File

@ -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<View.OnClickListener>
@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<Int>(), any()) } returns toast
every { Toast.makeText(context, any<String>(), 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<String>(), 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<String>(), 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<String>(), 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()) }
}
}