Add SpaAppBridgeActivity for application usage

This requires the user to pass into the package name, which enabling
the feature of navigating to app info pages.

Bug: 263553430
Test: Manually with Settings
Change-Id: I9405e3732d99f78cd87e62d73b0c9519a8e2d71f
This commit is contained in:
Chaohui Wang
2022-12-23 16:48:40 +08:00
parent fec083b2cd
commit a9c624b0d3
5 changed files with 138 additions and 9 deletions

View File

@@ -4744,6 +4744,7 @@
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
android:exported="false" />
<activity android:name=".spa.SpaBridgeActivity" android:exported="false"/>
<activity android:name=".spa.SpaAppBridgeActivity" android:exported="false"/>
<activity-alias android:name="UsageStatsActivity"
android:exported="true"

View File

@@ -18,16 +18,27 @@ package com.android.settings.spa
import android.content.Context
import android.content.Intent
import android.os.UserHandle
import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.util.appendSpaParams
class SpaActivity : BrowseActivity() {
companion object {
@JvmStatic
fun startSpaActivity(context: Context, destination: String) {
val intent = Intent(context, SpaActivity::class.java)
fun Context.startSpaActivity(destination: String) {
val intent = Intent(this, SpaActivity::class.java)
.appendSpaParams(destination = destination)
context.startActivity(intent)
startActivity(intent)
}
@JvmStatic
fun Context.startSpaActivityForApp(destinationPrefix: String, intent: Intent): Boolean {
val packageName = intent.data?.schemeSpecificPart ?: return false
val userId = intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE, UserHandle::class.java)
?.identifier
?: UserHandle.myUserId()
startSpaActivity("$destinationPrefix/$packageName/$userId")
return true
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa
import android.app.Activity
import android.os.Bundle
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settings.spa.SpaBridgeActivity.Companion.getDestination
/**
* Activity used as a bridge to [SpaActivity] with package scheme for application usage.
*
* Since [SpaActivity] is not exported, [SpaActivity] could not be the target activity of
* <activity-alias>, otherwise all its pages will be exported.
* So need this bridge activity to sit in the middle of <activity-alias> and [SpaActivity].
*/
class SpaAppBridgeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getDestination()?.let { destination ->
startSpaActivityForApp(destination, intent)
}
finish()
}
}

12
src/com/android/settings/spa/SpaBridgeActivity.kt Executable file → Normal file
View File

@@ -33,17 +33,17 @@ class SpaBridgeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getDestination()?.let { destination ->
startSpaActivity(this, destination)
startSpaActivity(destination)
}
finish()
}
private fun getDestination(): String? =
packageManager.getActivityInfo(
componentName, ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
).metaData.getString(META_DATA_KEY_DESTINATION)
companion object {
fun Activity.getDestination(): String? =
packageManager.getActivityInfo(
componentName, ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
).metaData.getString(META_DATA_KEY_DESTINATION)
private const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@RunWith(AndroidJUnit4::class)
class SpaActivityTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var context: Context
@Test
fun startSpaActivity() {
context.startSpaActivity(DESTINATION)
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivity(intentCaptor.capture())
val intent = intentCaptor.value
assertThat(intent.component?.className).isEqualTo(SpaActivity::class.qualifiedName)
assertThat(intent.getStringExtra(KEY_DESTINATION)).isEqualTo(DESTINATION)
}
@Test
fun startSpaActivityForApp() {
val intent = Intent().apply {
data = Uri.parse("package:$PACKAGE_NAME")
putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.of(USER_ID))
}
context.startSpaActivityForApp(DESTINATION, intent)
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivity(intentCaptor.capture())
val capturedIntent = intentCaptor.value
assertThat(capturedIntent.component?.className).isEqualTo(SpaActivity::class.qualifiedName)
assertThat(capturedIntent.getStringExtra(KEY_DESTINATION))
.isEqualTo("Destination/package.name/1")
}
private companion object {
const val DESTINATION = "Destination"
const val PACKAGE_NAME = "package.name"
const val USER_ID = 1
}
}