Merge "Add AppAllServicesPreference for Spa"

This commit is contained in:
Chaohui Wang
2022-10-31 11:18:43 +00:00
committed by Android (Google) Code Review
8 changed files with 328 additions and 37 deletions

View File

@@ -0,0 +1,116 @@
/*
* 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.app.appinfo
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Resources
import android.os.Bundle
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.resolveActionForApp
import com.android.settingslib.spaprivileged.model.app.userHandle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
@Composable
fun AppAllServicesPreference(app: ApplicationInfo) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val presenter = remember { AppAllServicesPresenter(context, app, coroutineScope) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_info_all_services_label)
override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
initialValue = stringResource(R.string.summary_placeholder),
)
override val onClick = presenter::startActivity
})
}
private class AppAllServicesPresenter(
private val context: Context,
private val app: ApplicationInfo,
private val coroutineScope: CoroutineScope,
) {
private val packageManager = context.packageManager
private val activityInfoFlow = flow {
emit(packageManager.resolveActionForApp(
app = app,
action = Intent.ACTION_VIEW_APP_FEATURES,
flags = PackageManager.GET_META_DATA,
))
}.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
val isAvailableFlow = activityInfoFlow.map { it != null }
val summaryFlow = activityInfoFlow.map { activityInfo ->
activityInfo?.metaData?.getSummary() ?: ""
}.flowOn(Dispatchers.IO)
private fun Bundle.getSummary(): String {
val resources = try {
packageManager.getResourcesForApplication(app)
} catch (exception: PackageManager.NameNotFoundException) {
Log.d(TAG, "Name not found for the application.")
return ""
}
return try {
resources.getString(getInt(SUMMARY_METADATA_KEY))
} catch (exception: Resources.NotFoundException) {
Log.d(TAG, "Resource not found for summary string.")
""
}
}
fun startActivity() {
coroutineScope.launch {
activityInfoFlow.firstOrNull()?.let { activityInfo ->
val intent = Intent(Intent.ACTION_VIEW_APP_FEATURES).apply {
component = activityInfo.componentName
}
context.startActivityAsUser(intent, app.userHandle)
}
}
}
companion object {
private const val TAG = "AppAllServicesPresenter"
private const val SUMMARY_METADATA_KEY = "app_features_preference_summary"
}
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.spa.app.appinfo
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.pm.ApplicationInfo
import android.util.Log
@@ -123,7 +122,7 @@ private class AppBatteryPresenter(private val context: Context, private val app:
Log.i(TAG, "handlePreferenceTreeClick():\n$this")
AdvancedPowerUsageDetail.startBatteryDetailPage(
context,
SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
AppInfoSettingsProvider.METRICS_CATEGORY,
this,
Utils.formatPercentage(percentOfTotal, true),
null,
@@ -141,7 +140,7 @@ private class AppBatteryPresenter(private val context: Context, private val app:
.setDestination(AdvancedPowerUsageDetail::class.java.name)
.setTitleRes(R.string.battery_details_title)
.setArguments(args)
.setSourceMetricsCategory(SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS)
.setSourceMetricsCategory(AppInfoSettingsProvider.METRICS_CATEGORY)
.launch()
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.spa.app.appinfo
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.pm.ApplicationInfo
import android.net.NetworkStats
@@ -122,7 +121,7 @@ private class AppDataUsagePresenter(
AppDataUsage::class.java,
app,
context,
SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}

View File

@@ -94,7 +94,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
AppButtons(packageInfoPresenter)
AppSettingsPreference(app)
// TODO: all_services_settings
AppAllServicesPreference(app)
// TODO: notification_settings
AppPermissionPreference(app)
AppStoragePreference(app)

View File

@@ -19,8 +19,8 @@ package com.android.settings.spa.app.appinfo
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager.ResolveInfoFlags
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -31,16 +31,17 @@ import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.resolveActionForApp
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.model.app.userId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.plus
@Composable
fun AppSettingsPreference(app: ApplicationInfo) {
@@ -63,39 +64,28 @@ private class AppSettingsPresenter(
private val packageManager = context.packageManager
private val intentFlow = flow {
emit(resolveIntent())
}.shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
emit(packageManager.resolveActionForApp(app, Intent.ACTION_APPLICATION_PREFERENCES))
}.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
val isAvailableFlow = intentFlow.map { it != null }
fun startActivity() {
coroutineScope.launch {
intentFlow.collect { intent ->
if (intent != null) {
FeatureFactory.getFactory(context).metricsFeatureProvider
.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_OPEN_APP_SETTING,
AppInfoSettingsProvider.METRICS_CATEGORY,
null,
0,
)
context.startActivityAsUser(intent, app.userHandle)
}
}
intentFlow.firstOrNull()?.let(::startActivity)
}
}
private suspend fun resolveIntent(): Intent? = withContext(Dispatchers.IO) {
private fun startActivity(activityInfo: ActivityInfo) {
FeatureFactory.getFactory(context).metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_OPEN_APP_SETTING,
AppInfoSettingsProvider.METRICS_CATEGORY,
null,
0,
)
val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES).apply {
`package` = app.packageName
component = activityInfo.componentName
}
packageManager.resolveActivityAsUser(intent, ResolveInfoFlags.of(0), app.userId)
?.activityInfo
?.let { activityInfo ->
Intent(intent.action).apply {
setClassName(activityInfo.packageName, activityInfo.name)
}
}
context.startActivityAsUser(intent, app.userHandle)
}
}