From c723f4bd0594c39596e93ee5f1f0076788a9a97e Mon Sep 17 00:00:00 2001 From: Brayan Oliveira <69634269+brayandso@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:24:32 -0300 Subject: [PATCH] refactor: move most of the preferences activity into a fragment --- AnkiDroid/src/main/AndroidManifest.xml | 2 +- .../java/com/ichi2/anki/CollectionHelper.kt | 3 +- .../main/java/com/ichi2/anki/DeckPicker.kt | 3 +- .../ichi2/anki/NavigationDrawerActivity.kt | 5 +- .../preferences/AdvancedSettingsFragment.kt | 7 - .../CustomButtonsSettingsFragment.kt | 8 - .../com/ichi2/anki/preferences/Preferences.kt | 171 +++++++++++------- .../anki/preferences/SettingsFragment.kt | 10 - AnkiDroid/src/main/res/layout/preferences.xml | 4 +- .../anki/ActivityStartupUnderBackupTest.kt | 4 +- .../ichi2/anki/preferences/PreferencesTest.kt | 21 ++- .../anki/preferences/SettingsSearchBarTest.kt | 5 +- .../java/com/ichi2/testutils/ActivityList.kt | 4 +- 13 files changed, 136 insertions(+), 111 deletions(-) diff --git a/AnkiDroid/src/main/AndroidManifest.xml b/AnkiDroid/src/main/AndroidManifest.xml index 93b3e6f37cd1..ba8d6f6cc241 100644 --- a/AnkiDroid/src/main/AndroidManifest.xml +++ b/AnkiDroid/src/main/AndroidManifest.xml @@ -346,7 +346,7 @@ android:configChanges="orientation|screenSize" android:exported="false" /> diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CollectionHelper.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CollectionHelper.kt index 4a5c4452ae2d..508e4e5d2248 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CollectionHelper.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CollectionHelper.kt @@ -25,7 +25,6 @@ import androidx.core.content.edit import com.ichi2.anki.AnkiDroidFolder.AppPrivateFolder import com.ichi2.anki.exception.StorageAccessException import com.ichi2.anki.exception.UnknownDatabaseVersionException -import com.ichi2.anki.preferences.Preferences import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.libanki.Collection import com.ichi2.libanki.DB @@ -47,7 +46,7 @@ object CollectionHelper { * This directory contains all AnkiDroid data and media for a given collection * Except the Android preferences, cached files and [MetaDB] * - * This can be changed by the [Preferences] screen + * This can be changed by the Preferences screen * to allow a user to access a second collection via the same AnkiDroid app instance. * * The path also defines the collection that the AnkiDroid API accesses diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 1b3ec8821bc6..df20b6cedc60 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -141,6 +141,7 @@ import com.ichi2.anki.pages.AnkiPackageImporterFragment import com.ichi2.anki.pages.CongratsPage import com.ichi2.anki.pages.CongratsPage.Companion.onDeckCompleted import com.ichi2.anki.preferences.AdvancedSettingsFragment +import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.receiver.SdCardReceiver import com.ichi2.anki.servicelayer.ScopedStorageService @@ -788,7 +789,7 @@ open class DeckPicker : convertDpToPixel(32F, this@DeckPicker).toInt() ) positiveButton(R.string.open_settings) { - val settingsIntent = AdvancedSettingsFragment.getSubscreenIntent(this@DeckPicker) + val settingsIntent = PreferencesActivity.getIntent(this@DeckPicker, AdvancedSettingsFragment::class) requestPathUpdateLauncher.launch(settingsIntent) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt index 61c24b05301c..9369d771be28 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt @@ -41,7 +41,7 @@ import androidx.drawerlayout.widget.DrawerLayout import com.google.android.material.color.MaterialColors import com.google.android.material.navigation.NavigationView import com.ichi2.anki.dialogs.help.HelpDialog -import com.ichi2.anki.preferences.Preferences +import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.workarounds.FullDraggableContainerFix import com.ichi2.compat.CompatHelper @@ -357,10 +357,9 @@ abstract class NavigationDrawerActivity : /** * Opens AnkiDroid's Settings Screen. - * @see Preferences */ protected fun openSettings() { - val intent = Intent(this, Preferences::class.java) + val intent = PreferencesActivity.getIntent(this) preferencesLauncher.launch(intent) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt index 85415c62b9e4..ea3f2e7fefb0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt @@ -16,7 +16,6 @@ package com.ichi2.anki.preferences import android.content.ComponentName -import android.content.Context import android.content.Intent import android.content.pm.PackageManager import androidx.appcompat.app.AlertDialog @@ -142,10 +141,4 @@ class AdvancedSettingsFragment : SettingsFragment() { } } } - - companion object { - fun getSubscreenIntent(context: Context): Intent { - return getSubscreenIntent(context, AdvancedSettingsFragment::class) - } - } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt index 879e56ebac8f..ed7d8002906c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt @@ -15,8 +15,6 @@ */ package com.ichi2.anki.preferences -import android.content.Context -import android.content.Intent import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AlertDialog import androidx.core.content.edit @@ -59,10 +57,4 @@ class CustomButtonsSettingsFragment : SettingsFragment() { fun allKeys(): HashSet { return allPreferences().mapTo(hashSetOf()) { it.key } } - - companion object { - fun getSubscreenIntent(context: Context): Intent { - return getSubscreenIntent(context, CustomButtonsSettingsFragment::class) - } - } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt index 2d1157ff87f4..1be8f34f3702 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt @@ -19,80 +19,73 @@ ****************************************************************************************/ package com.ichi2.anki.preferences +import android.content.Context +import android.content.Intent import android.os.Bundle -import android.view.MenuItem +import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.annotation.XmlRes +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.commit import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import com.bytehamster.lib.preferencesearch.SearchConfiguration import com.bytehamster.lib.preferencesearch.SearchPreferenceResult import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.CollapsingToolbarLayout -import com.ichi2.anki.AnkiActivity +import com.google.android.material.appbar.MaterialToolbar import com.ichi2.anki.R +import com.ichi2.anki.SingleFragmentActivity import com.ichi2.anki.utils.isWindowCompact -import com.ichi2.themes.setTransparentStatusBar import com.ichi2.utils.getInstanceFromClassName import timber.log.Timber +import kotlin.reflect.KClass import kotlin.reflect.jvm.jvmName -class Preferences : - AnkiActivity(), +class PreferencesFragment : + Fragment(R.layout.preferences), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, SearchPreferenceResultListener { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.preferences) - setTransparentStatusBar() - - enableToolbar().setDisplayHomeAsUpEnabled(true) - - // Load initial fragment if activity is being first created - if (savedInstanceState == null) { - loadInitialFragment() - } - supportFragmentManager.addOnBackStackChangedListener { - // Expand bar in new fragments if scrolled to top - val fragment = supportFragmentManager.findFragmentById(R.id.settings_container) - as? PreferenceFragmentCompat ?: return@addOnBackStackChangedListener - fragment.listView.post { - val viewHolder = fragment.listView?.findViewHolderForAdapterPosition(0) - val isAtTop = viewHolder != null && viewHolder.itemView.top >= 0 - findViewById(R.id.appbar).setExpanded(isAtTop, false) + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (childFragmentManager.backStackEntryCount > 0) { + childFragmentManager.popBackStack() + } else { + requireActivity().finish() } - - val title = if (fragment is TitleProvider) fragment.title else "" - findViewById(R.id.collapsingToolbarLayout)?.title = title - supportActionBar?.title = title } } - /** - * Starts the first fragment for the [Preferences] activity, - * which by default is [HeaderFragment]. - * The initial fragment may be overridden by putting the java class name - * of the fragment on an intent extra with the key [INITIAL_FRAGMENT_EXTRA] - */ - private fun loadInitialFragment() { - val fragmentClassName = intent?.getStringExtra(INITIAL_FRAGMENT_EXTRA) - val initialFragment = if (fragmentClassName == null) { - if (resources.isWindowCompact()) HeaderFragment() else GeneralSettingsFragment() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + view.findViewById(R.id.toolbar) + .setNavigationOnClickListener { onBackPressedCallback.handleOnBackPressed() } + + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback) + + // Load initial subscreen if activity is being first created + if (savedInstanceState == null) { + loadInitialSubscreen() } else { - try { - getInstanceFromClassName(fragmentClassName) - } catch (e: Exception) { - throw RuntimeException("Failed to load $fragmentClassName", e) + childFragmentManager.findFragmentById(R.id.settings_container)?.let { + setFragmentTitleOnToolbar(it) } } - supportFragmentManager.commit { - // In tablets, show the headers fragment at the lateral navigation container - if (!resources.isWindowCompact()) { - replace(R.id.lateral_nav_container, HeaderFragment()) + + childFragmentManager.addOnBackStackChangedListener { + val fragment = childFragmentManager.findFragmentById(R.id.settings_container) + ?: return@addOnBackStackChangedListener + + setFragmentTitleOnToolbar(fragment) + + // Expand bar in new fragments if scrolled to top + (fragment as? PreferenceFragmentCompat)?.listView?.post { + val viewHolder = fragment.listView?.findViewHolderForAdapterPosition(0) + val isAtTop = viewHolder != null && viewHolder.itemView.top >= 0 + view.findViewById(R.id.appbar).setExpanded(isAtTop, false) } - replace(R.id.settings_container, initialFragment, initialFragment::class.java.name) } } @@ -101,42 +94,27 @@ class Preferences : pref: Preference ): Boolean { // avoid reopening the same fragment if already active - val currentFragment = supportFragmentManager.findFragmentById(R.id.settings_container) + val currentFragment = childFragmentManager.findFragmentById(R.id.settings_container) ?: return true if (pref.fragment == currentFragment::class.jvmName) return true - val fragment = supportFragmentManager.fragmentFactory.instantiate( - classLoader, + val fragment = childFragmentManager.fragmentFactory.instantiate( + requireActivity().classLoader, pref.fragment ?: return true ) fragment.arguments = pref.extras - supportFragmentManager.commit { + childFragmentManager.commit { replace(R.id.settings_container, fragment, fragment::class.jvmName) addToBackStack(null) } return true } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - if (resources.isWindowCompact()) { - onBackPressedDispatcher.onBackPressed() - } else { - finish() - } - } - return true - } - - // ---------------------------------------------------------------------------- - // Class methods - // ---------------------------------------------------------------------------- - override fun onSearchResultClicked(result: SearchPreferenceResult) { val fragment = getFragmentFromXmlRes(result.resourceFile) ?: return - supportFragmentManager.popBackStack() // clear the search fragment from the backstack - supportFragmentManager.commit { + parentFragmentManager.popBackStack() // clear the search fragment from the backstack + childFragmentManager.commit { replace(R.id.settings_container, fragment, fragment.javaClass.name) addToBackStack(fragment.javaClass.name) } @@ -144,6 +122,63 @@ class Preferences : Timber.i("Highlighting key '%s' on %s", result.key, fragment) result.highlight(fragment as PreferenceFragmentCompat) } + + private fun setFragmentTitleOnToolbar(fragment: Fragment) { + val title = if (fragment is TitleProvider) fragment.title else getString(R.string.settings) + + view?.findViewById(R.id.collapsingToolbarLayout)?.title = title + view?.findViewById(R.id.toolbar)?.title = title + } + + /** + * Starts the first settings fragment, which by default is [HeaderFragment]. + * The initial fragment may be overridden by putting the java class name + * of the fragment on an intent extra with the key [INITIAL_FRAGMENT_EXTRA] + */ + private fun loadInitialSubscreen() { + val fragmentClassName = arguments?.getString(INITIAL_FRAGMENT_EXTRA) + val initialFragment = if (fragmentClassName == null) { + if (resources.isWindowCompact()) HeaderFragment() else GeneralSettingsFragment() + } else { + try { + getInstanceFromClassName(fragmentClassName) + } catch (e: Exception) { + throw RuntimeException("Failed to load $fragmentClassName", e) + } + } + childFragmentManager.commit { + // In big screens, show the headers fragment at the lateral navigation container + if (!resources.isWindowCompact()) { + replace(R.id.lateral_nav_container, HeaderFragment()) + } + replace(R.id.settings_container, initialFragment, initialFragment::class.java.name) + } + } +} + +/** + * Host activity for [PreferencesFragment]. + * + * Only necessary because [SearchConfiguration] demands an activity that implements + * [SearchPreferenceResultListener]. + */ +class PreferencesActivity : SingleFragmentActivity(), SearchPreferenceResultListener { + override fun onSearchResultClicked(result: SearchPreferenceResult) { + val fragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) + if (fragment is SearchPreferenceResultListener) { + fragment.onSearchResultClicked(result) + } + } + + companion object { + fun getIntent(context: Context, initialFragment: KClass? = null): Intent { + val arguments = bundleOf(INITIAL_FRAGMENT_EXTRA to initialFragment?.jvmName) + return Intent(context, PreferencesActivity::class.java).apply { + putExtra(FRAGMENT_NAME_EXTRA, PreferencesFragment::class.jvmName) + putExtra(FRAGMENT_ARGS_EXTRA, arguments) + } + } + } } interface TitleProvider { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SettingsFragment.kt index a517e639dcaf..565a3b9a6b47 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SettingsFragment.kt @@ -15,8 +15,6 @@ */ package com.ichi2.anki.preferences -import android.content.Context -import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import androidx.annotation.VisibleForTesting @@ -31,8 +29,6 @@ import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.preferences.DialogFragmentProvider import timber.log.Timber import java.lang.NumberFormatException -import kotlin.reflect.KClass -import kotlin.reflect.jvm.jvmName abstract class SettingsFragment : PreferenceFragmentCompat(), @@ -132,12 +128,6 @@ abstract class SettingsFragment : } companion object { - @JvmStatic // Using protected members which are not @JvmStatic in the superclass companion is unsupported yet - protected fun getSubscreenIntent(context: Context, fragmentClass: KClass): Intent { - return Intent(context, Preferences::class.java) - .putExtra(INITIAL_FRAGMENT_EXTRA, fragmentClass.jvmName) - } - /** * Converts a preference value to a numeric number that * can be reported to analytics, since analytics events only accept diff --git a/AnkiDroid/src/main/res/layout/preferences.xml b/AnkiDroid/src/main/res/layout/preferences.xml index b443e5a86168..c71d52ba2f07 100644 --- a/AnkiDroid/src/main/res/layout/preferences.xml +++ b/AnkiDroid/src/main/res/layout/preferences.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" - tools:context=".preferences.Preferences"> + tools:context=".preferences.PreferencesFragment"> + app:title="@string/settings"/> diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt index b236c712e037..0d06d84828ad 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt @@ -18,7 +18,7 @@ package com.ichi2.anki import android.app.Activity import android.os.Looper.getMainLooper import com.ichi2.anki.instantnoteeditor.InstantNoteEditorActivity -import com.ichi2.anki.preferences.Preferences +import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.testutils.ActivityList import com.ichi2.testutils.ActivityList.ActivityLaunchParam import com.ichi2.testutils.EmptyApplication @@ -51,7 +51,7 @@ class ActivityStartupUnderBackupTest : RobolectricTest() { fun before() { notYetHandled(IntentHandler::class.java.simpleName, "Not working (or implemented) - inherits from Activity") notYetHandled(IntentHandler2::class.java.simpleName, "Not working (or implemented) - inherits from Activity") - notYetHandled(Preferences::class.java.simpleName, "Not working (or implemented) - inherits from AppCompatPreferenceActivity") + notYetHandled(PreferencesActivity::class.java.simpleName, "Not working (or implemented) - inherits from AppCompatPreferenceActivity") notYetHandled(FilteredDeckOptions::class.java.simpleName, "Not working (or implemented) - inherits from AppCompatPreferenceActivity") notYetHandled(SingleFragmentActivity::class.java.simpleName, "Implemented, but the test fails because the activity throws if a specific intent extra isn't set") notYetHandled(InstantNoteEditorActivity::class.java.simpleName, "Single instance activity so should be used") diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt index 38c04f212dc3..c27a41dd32f5 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt @@ -31,15 +31,16 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config +import kotlin.reflect.jvm.jvmName @RunWith(AndroidJUnit4::class) class PreferencesTest : RobolectricTest() { - private lateinit var preferences: Preferences + private lateinit var preferences: PreferencesActivity @Before override fun setUp() { super.setUp() - preferences = Preferences() + preferences = PreferencesActivity() val attachBaseContext = getJavaMethodAsAccessible( AppCompatActivity::class.java, "attachBaseContext", @@ -72,7 +73,7 @@ class PreferencesTest : RobolectricTest() { /** checks if any of the Preferences fragments throws while being created */ @Test fun fragmentsDoNotThrowOnCreation() { - val activityScenario = ActivityScenario.launch(Preferences::class.java) + val activityScenario = ActivityScenario.launch(PreferencesActivity.getIntent(targetContext)) activityScenario.onActivity { activity -> PreferenceTestUtils.getAllPreferencesFragments(activity).forEach { @@ -83,6 +84,20 @@ class PreferencesTest : RobolectricTest() { } } + @Test + fun `All preferences fragments are TitleProvider`() { + val fragments = PreferenceTestUtils.getAllPreferencesFragments(targetContext) + .filter { it !is ReviewerOptionsFragment } // WIP dev options + + fragments.forEach { fragment -> + assertThat( + "${fragment::class.jvmName} should implement TitleProvider", + fragment is TitleProvider, + equalTo(true) + ) + } + } + @Test @Config(qualifiers = "ar") fun buildHeaderSummary_RTL_Test() { diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt index 31f38a9cf6be..cbe54c7efed7 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt @@ -73,8 +73,9 @@ class SettingsSearchBarTest : RobolectricTest() { } } - private fun getPreferencesActivity(): Preferences { - return Robolectric.buildActivity(Preferences::class.java) + private fun getPreferencesActivity(): PreferencesActivity { + val intent = PreferencesActivity.getIntent(targetContext) + return Robolectric.buildActivity(PreferencesActivity::class.java, intent) .create().start().resume().get() } } diff --git a/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt b/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt index 061daa079bb9..48da2b582864 100644 --- a/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt +++ b/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt @@ -41,7 +41,7 @@ import com.ichi2.anki.StudyOptionsActivity import com.ichi2.anki.instantnoteeditor.InstantNoteEditorActivity import com.ichi2.anki.multimedia.MultimediaActivity import com.ichi2.anki.notetype.ManageNotetypes -import com.ichi2.anki.preferences.Preferences +import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.anki.previewer.CardViewerActivity import com.ichi2.anki.services.ReminderService.Companion.getReviewDeckIntent import com.ichi2.anki.ui.windows.managespace.ManageSpaceActivity @@ -74,7 +74,7 @@ object ActivityList { // Likely has unhandled intents get(Reviewer::class.java), get(MyAccount::class.java), - get(Preferences::class.java), + get(PreferencesActivity::class.java), get(FilteredDeckOptions::class.java), get(DrawingActivity::class.java), // Info has unhandled intents