Add SpaEnvironmentFactory

To support different config for different Settings App.
Set SpaEnvironment instance in application.onCreate
Get SpaEnvironment instance from SpaEnvironmentFactory, rather than pass through parameter.

Bug: 244122804
Test: manual - build Gallery & Settings
Change-Id: I15600a085c1b7c63d37399e85aa8c71659cccc4e
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index e583138..0a4972f 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -18,12 +18,13 @@
     package="com.android.settingslib.spa.gallery">
 
     <application
+        android:name=".GalleryApplication"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_label"
         android:supportsRtl="true"
         android:enableOnBackInvokedCallback="true">
         <activity
-            android:name=".MainActivity"
+            android:name=".GalleryMainActivity"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
similarity index 70%
copy from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
copy to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
index 5e859ce..8c9d42c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryApplication.kt
@@ -16,6 +16,12 @@
 
 package com.android.settingslib.spa.gallery
 
-import com.android.settingslib.spa.framework.BrowseActivity
+import android.app.Application
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 
-class MainActivity : BrowseActivity(GallerySpaEnvironment)
+class GalleryApplication : Application() {
+    override fun onCreate() {
+        super.onCreate()
+        SpaEnvironmentFactory.instance = GallerySpaEnvironment
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 332d5a8..23072a2 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.DebugActivity
 
-class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
+class GalleryDebugActivity : DebugActivity()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
index 5e04861..817c209f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.EntryProvider
 
-class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
+class GalleryEntryProvider : EntryProvider()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
similarity index 92%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
index 5e859ce..08a9bf5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryMainActivity.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.BrowseActivity
 
-class MainActivity : BrowseActivity(GallerySpaEnvironment)
+class GalleryMainActivity : BrowseActivity()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 33c4d77..aa457fe 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -87,7 +87,7 @@
         )
     }
 
-    override val browseActivityClass = MainActivity::class.java
+    override val browseActivityClass = GalleryMainActivity::class.java
 
     override val entryProviderAuthorities = "com.android.spa.gallery.provider"
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 8ca1c37..d87c31b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -30,7 +30,7 @@
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.rememberNavController
 import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.compose.LocalNavController
 import com.android.settingslib.spa.framework.compose.NavControllerWrapperImpl
 import com.android.settingslib.spa.framework.compose.localNavController
@@ -50,8 +50,8 @@
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
  */
-open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
-    private val sppRepository by spaEnvironment.pageProviderRepository
+open class BrowseActivity : ComponentActivity() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
@@ -67,6 +67,7 @@
 
     @Composable
     private fun MainContent() {
+        val sppRepository by spaEnvironment.pageProviderRepository
         val navController = rememberNavController()
         CompositionLocalProvider(navController.localNavController()) {
             NavHost(navController, NULL_PAGE_NAME) {
@@ -84,6 +85,7 @@
 
     @Composable
     private fun InitialDestinationNavigator() {
+        val sppRepository by spaEnvironment.pageProviderRepository
         val destinationNavigated = rememberSaveable { mutableStateOf(false) }
         if (destinationNavigated.value) return
         destinationNavigated.value = true
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index ab7c0fe1..b28da06 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -37,7 +37,7 @@
 import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_HIGHLIGHT_ENTRY
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsPage
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.compose.localNavController
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.toState
@@ -63,8 +63,8 @@
  * For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
  * For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
  */
-open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
-    private val entryRepository by spaEnvironment.entryRepository
+open class DebugActivity : ComponentActivity() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
@@ -128,6 +128,7 @@
 
     @Composable
     fun RootPage() {
+        val entryRepository by spaEnvironment.entryRepository
         val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
         val allEntry = remember { entryRepository.getAllEntries() }
         HomeScaffold(title = "Settings Debug") {
@@ -141,6 +142,7 @@
             })
             Preference(object : PreferenceModel {
                 override val title = "Query EntryProvider"
+                override val enabled = isEntryProviderAvailable().toState()
                 override val onClick = { displayDebugMessage() }
             })
         }
@@ -148,6 +150,7 @@
 
     @Composable
     fun AllPages() {
+        val entryRepository by spaEnvironment.entryRepository
         val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
         RegularScaffold(title = "All Pages (${allPageWithEntry.size})") {
             for (pageWithEntry in allPageWithEntry) {
@@ -164,6 +167,7 @@
 
     @Composable
     fun AllEntries() {
+        val entryRepository by spaEnvironment.entryRepository
         val allEntry = remember { entryRepository.getAllEntries() }
         RegularScaffold(title = "All Entries (${allEntry.size})") {
             EntryList(allEntry)
@@ -172,6 +176,7 @@
 
     @Composable
     fun OnePage(arguments: Bundle?) {
+        val entryRepository by spaEnvironment.entryRepository
         val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
         val pageWithEntry = entryRepository.getPageWithEntry(id)!!
         RegularScaffold(title = "Page - ${pageWithEntry.page.displayName}") {
@@ -180,7 +185,7 @@
             Text(text = "Entry size: ${pageWithEntry.entries.size}")
             Preference(model = object : PreferenceModel {
                 override val title = "open page"
-                override val enabled = (!pageWithEntry.page.hasRuntimeParam()).toState()
+                override val enabled = isPageClickable(pageWithEntry.page).toState()
                 override val onClick = openPage(pageWithEntry.page)
             })
             EntryList(pageWithEntry.entries)
@@ -189,13 +194,14 @@
 
     @Composable
     fun OneEntry(arguments: Bundle?) {
+        val entryRepository by spaEnvironment.entryRepository
         val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
         val entry = entryRepository.getEntry(id)!!
         val entryContent = remember { entry.formatContent() }
         RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
             Preference(model = object : PreferenceModel {
                 override val title = "open entry"
-                override val enabled = (!entry.containerPage().hasRuntimeParam()).toState()
+                override val enabled = isEntryClickable(entry).toState()
                 override val onClick = openEntry(entry)
             })
             Text(text = entryContent)
@@ -216,7 +222,7 @@
 
     @Composable
     private fun openPage(page: SettingsPage): (() -> Unit)? {
-        if (page.hasRuntimeParam()) return null
+        if (!isPageClickable(page)) return null
         val context = LocalContext.current
         val route = page.buildRoute()
         val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
@@ -230,7 +236,7 @@
 
     @Composable
     private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
-        if (entry.containerPage().hasRuntimeParam()) return null
+        if (!isEntryClickable(entry)) return null
         val context = LocalContext.current
         val route = entry.containerPage().buildRoute()
         val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
@@ -242,4 +248,17 @@
             context.startActivity(intent)
         }
     }
+
+    private fun isEntryProviderAvailable(): Boolean {
+        return spaEnvironment.entryProviderAuthorities != null
+    }
+
+    private fun isPageClickable(page: SettingsPage): Boolean {
+        return spaEnvironment.browseActivityClass != null && !page.hasRuntimeParam()
+    }
+
+    private fun isEntryClickable(entry: SettingsEntry): Boolean {
+        return spaEnvironment.browseActivityClass != null &&
+            !entry.containerPage().hasRuntimeParam()
+    }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index 50157fc..532f63b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -30,7 +30,7 @@
 import android.util.Log
 import com.android.settingslib.spa.framework.common.SettingsEntry
 import com.android.settingslib.spa.framework.common.SettingsPage
-import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 
 private const val TAG = "EntryProvider"
 
@@ -49,9 +49,8 @@
  *   $ adb shell content query --uri content://<AuthorityPath>/search_static
  *   $ adb shell content query --uri content://<AuthorityPath>/search_dynamic
  */
-open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
-    private val entryRepository by spaEnvironment.entryRepository
-    private val browseActivityClass = spaEnvironment.browseActivityClass
+open class EntryProvider : ContentProvider() {
+    private val spaEnvironment get() = SpaEnvironmentFactory.instance
 
     /**
      * Enum to define all column names in provider.
@@ -221,6 +220,7 @@
     }
 
     private fun queryPageDebug(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
         for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
             val command = createBrowsePageAdbCommand(pageWithEntry.page)
@@ -232,6 +232,7 @@
     }
 
     private fun queryEntryDebug(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             val command = createBrowsePageAdbCommand(entry.containerPage(), entry.id)
@@ -243,6 +244,7 @@
     }
 
     private fun queryPageInfo(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
         for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
             val page = pageWithEntry.page
@@ -261,6 +263,7 @@
     }
 
     private fun queryEntryInfo(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             cursor.newRow()
@@ -276,6 +279,7 @@
     }
 
     private fun querySearchSitemap(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_SITEMAP_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch) continue
@@ -287,6 +291,7 @@
     }
 
     private fun querySearchStaticData(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_STATIC_DATA_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch || entry.isSearchDataDynamic) continue
@@ -296,6 +301,7 @@
     }
 
     private fun querySearchDynamicData(): Cursor {
+        val entryRepository by spaEnvironment.entryRepository
         val cursor = MatrixCursor(QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.getColumns())
         for (entry in entryRepository.getAllEntries()) {
             if (!entry.isAllowSearch || !entry.isSearchDataDynamic) continue
@@ -317,26 +323,31 @@
     }
 
     private fun createBrowsePageIntent(page: SettingsPage, entryId: String? = null): Intent {
-        if (context == null || page.hasRuntimeParam())
-            return Intent()
-
-        return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
-            putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
-            if (entryId != null) {
-                putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
+        if (!isPageBrowsable(page)) return Intent()
+        return Intent().setComponent(ComponentName(context!!, spaEnvironment.browseActivityClass!!))
+            .apply {
+                putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
+                if (entryId != null) {
+                    putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
+                }
             }
-        }
     }
 
     private fun createBrowsePageAdbCommand(page: SettingsPage, entryId: String? = null): String? {
-        if (context == null || page.hasRuntimeParam()) return null
+        if (!isPageBrowsable(page)) return null
         val packageName = context!!.packageName
-        val activityName = browseActivityClass.name.replace(packageName, "")
+        val activityName = spaEnvironment.browseActivityClass!!.name.replace(packageName, "")
         val destinationParam = " -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
         val highlightParam =
             if (entryId != null) " -e ${BrowseActivity.KEY_HIGHLIGHT_ENTRY} $entryId" else ""
         return "adb shell am start -n $packageName/$activityName$destinationParam$highlightParam"
     }
+
+    private fun isPageBrowsable(page: SettingsPage): Boolean {
+        return context != null &&
+            spaEnvironment.browseActivityClass != null &&
+            !page.hasRuntimeParam()
+    }
 }
 
 fun EntryProvider.QueryEnum.getColumns(): Array<String> {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 111555b..3885025 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -18,12 +18,28 @@
 
 import android.app.Activity
 
+object SpaEnvironmentFactory {
+    private var spaEnvironment: SpaEnvironment? = null
+
+    var instance: SpaEnvironment
+        get() {
+            if (spaEnvironment == null)
+                throw UnsupportedOperationException("Spa environment is not set")
+            return spaEnvironment!!
+        }
+        set(env: SpaEnvironment) {
+            if (spaEnvironment != null)
+                throw UnsupportedOperationException("Spa environment is already set")
+            spaEnvironment = env
+        }
+}
+
 abstract class SpaEnvironment {
     abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
 
     val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
 
-    abstract val browseActivityClass: Class<out Activity>
+    open val browseActivityClass: Class<out Activity>? = null
 
     open val entryProviderAuthorities: String? = null