Migrate mainline module version preference
Bug: 365886251 Flag: com.android.settings.flags.catalyst_firmware_version Test: Unit Change-Id: I1f3f69b08fe22c66d1b1c6786f9cd7e7ff413529
This commit is contained in:
@@ -49,7 +49,7 @@ class FirmwareVersionScreen : PreferenceScreenCreator, PreferenceSummaryProvider
|
|||||||
preferenceHierarchy(this) {
|
preferenceHierarchy(this) {
|
||||||
+PreferenceWidget("os_firmware_version", R.string.firmware_version)
|
+PreferenceWidget("os_firmware_version", R.string.firmware_version)
|
||||||
+PreferenceWidget("security_key", R.string.security_patch)
|
+PreferenceWidget("security_key", R.string.security_patch)
|
||||||
+PreferenceWidget("module_version", R.string.module_version)
|
+MainlineModuleVersionPreference()
|
||||||
+BasebandVersionPreference()
|
+BasebandVersionPreference()
|
||||||
+KernelVersionPreference()
|
+KernelVersionPreference()
|
||||||
+SimpleBuildNumberPreference()
|
+SimpleBuildNumberPreference()
|
||||||
|
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.deviceinfo.firmwareversion
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.utils.getLocale
|
||||||
|
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
|
||||||
|
import com.android.settingslib.metadata.PreferenceMetadata
|
||||||
|
import com.android.settingslib.metadata.PreferenceSummaryProvider
|
||||||
|
import com.android.settingslib.preference.PreferenceBinding
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.TimeZone
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
|
class MainlineModuleVersionPreference :
|
||||||
|
PreferenceMetadata,
|
||||||
|
PreferenceSummaryProvider,
|
||||||
|
PreferenceAvailabilityProvider,
|
||||||
|
PreferenceBinding {
|
||||||
|
|
||||||
|
private var moduleVersion: String? = null
|
||||||
|
|
||||||
|
override val key: String
|
||||||
|
get() = "module_version"
|
||||||
|
|
||||||
|
override val title: Int
|
||||||
|
get() = R.string.module_version
|
||||||
|
|
||||||
|
override fun getSummary(context: Context): CharSequence? {
|
||||||
|
val version = getModuleVersion(context)
|
||||||
|
if (version.isEmpty()) return null
|
||||||
|
|
||||||
|
val locale = context.getLocale()
|
||||||
|
fun parseDate(pattern: String): Date? {
|
||||||
|
val simpleDateFormat = SimpleDateFormat(pattern, locale)
|
||||||
|
simpleDateFormat.timeZone = TimeZone.getDefault()
|
||||||
|
return try {
|
||||||
|
simpleDateFormat.parse(version)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val date = parseDate("yyyy-MM-dd") ?: parseDate("yyyy-MM")
|
||||||
|
return if (date == null) {
|
||||||
|
Log.w(TAG, "Cannot parse mainline versionName ($version) as date")
|
||||||
|
version
|
||||||
|
} else {
|
||||||
|
DateFormat.format(DateFormat.getBestDateTimePattern(locale, "dMMMMyyyy"), date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun intent(context: Context): Intent? {
|
||||||
|
val packageManager = context.packageManager
|
||||||
|
val intentPackage =
|
||||||
|
if (Flags.mainlineModuleExplicitIntent()) {
|
||||||
|
context.getString(R.string.config_mainline_module_update_package)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
fun String.resolveIntent() =
|
||||||
|
Intent(this).let {
|
||||||
|
if (intentPackage != null) it.setPackage(intentPackage)
|
||||||
|
if (packageManager.resolveActivity(it, 0) != null) it else null
|
||||||
|
}
|
||||||
|
|
||||||
|
return MODULE_UPDATE_ACTION_V2.resolveIntent() ?: MODULE_UPDATE_ACTION.resolveIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAvailable(context: Context) = getModuleVersion(context).isNotEmpty()
|
||||||
|
|
||||||
|
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
|
||||||
|
super.bind(preference, metadata)
|
||||||
|
preference.isSelectable = preference.intent != null
|
||||||
|
preference.isCopyingEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getModuleVersion(context: Context): String =
|
||||||
|
moduleVersion ?: context.getVersion().also { moduleVersion = it }
|
||||||
|
|
||||||
|
private fun Context.getVersion(): String {
|
||||||
|
val moduleProvider =
|
||||||
|
getString(com.android.internal.R.string.config_defaultModuleMetadataProvider)
|
||||||
|
if (moduleProvider.isEmpty()) return ""
|
||||||
|
return try {
|
||||||
|
packageManager.getPackageInfo(moduleProvider, 0)?.versionName ?: ""
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
Log.e(TAG, "Failed to get mainline version.", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MainlineModulePreference"
|
||||||
|
const val MODULE_UPDATE_ACTION = "android.settings.MODULE_UPDATE_SETTINGS"
|
||||||
|
const val MODULE_UPDATE_ACTION_V2 = "android.settings.MODULE_UPDATE_VERSIONS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LINT.ThenChange(MainlineModuleVersionPreferenceController.java)
|
@@ -39,6 +39,7 @@ import java.util.Locale;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
public class MainlineModuleVersionPreferenceController extends BasePreferenceController {
|
public class MainlineModuleVersionPreferenceController extends BasePreferenceController {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -141,3 +142,4 @@ public class MainlineModuleVersionPreferenceController extends BasePreferenceCon
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// LINT.ThenChange(MainlineModuleVersionPreference.kt)
|
||||||
|
27
src/com/android/settings/utils/ContextUtils.kt
Normal file
27
src/com/android/settings/utils/ContextUtils.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
/** Returns the locale of context. */
|
||||||
|
fun Context.getLocale(): Locale {
|
||||||
|
val configuration = resources.configuration ?: return Locale.getDefault()
|
||||||
|
val locales = configuration.locales
|
||||||
|
return if (locales.isEmpty) configuration.locale else locales.get(0)
|
||||||
|
}
|
@@ -47,6 +47,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public class MainlineModuleVersionPreferenceControllerTest {
|
public class MainlineModuleVersionPreferenceControllerTest {
|
||||||
|
|
||||||
@@ -222,3 +223,4 @@ public class MainlineModuleVersionPreferenceControllerTest {
|
|||||||
when(mPackageManager.getPackageInfo(eq(provider), anyInt())).thenReturn(info);
|
when(mPackageManager.getPackageInfo(eq(provider), anyInt())).thenReturn(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// LINT.ThenChange(MainlineModuleVersionPreferenceTest.kt)
|
||||||
|
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.deviceinfo.firmwareversion
|
||||||
|
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.platform.test.annotations.DisableFlags
|
||||||
|
import android.platform.test.annotations.EnableFlags
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.deviceinfo.firmwareversion.MainlineModuleVersionPreference.Companion.MODULE_UPDATE_ACTION
|
||||||
|
import com.android.settings.deviceinfo.firmwareversion.MainlineModuleVersionPreference.Companion.MODULE_UPDATE_ACTION_V2
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.any
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.eq
|
||||||
|
import org.mockito.kotlin.KStubbing
|
||||||
|
import org.mockito.kotlin.doAnswer
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.doThrow
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
|
||||||
|
// LINT.IfChange
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class MainlineModuleVersionPreferenceTest {
|
||||||
|
@get:Rule val setFlagsRule = SetFlagsRule()
|
||||||
|
|
||||||
|
private lateinit var mockPackageManager: PackageManager
|
||||||
|
private lateinit var mockResources: Resources
|
||||||
|
|
||||||
|
private val context =
|
||||||
|
object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
|
||||||
|
override fun getPackageManager(): PackageManager = mockPackageManager
|
||||||
|
|
||||||
|
override fun getResources(): Resources = mockResources
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mainlineModuleVersionPreference = MainlineModuleVersionPreference()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isAvailable_noMainlineModuleProvider_unavailable() {
|
||||||
|
createMocks("", null)
|
||||||
|
assertThat(mainlineModuleVersionPreference.isAvailable(context)).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isAvailable_noMainlineModulePackageInfo_unavailable() {
|
||||||
|
createMocks("test.provider", PackageManager.NameNotFoundException())
|
||||||
|
assertThat(mainlineModuleVersionPreference.isAvailable(context)).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isAvailable_hasMainlineModulePackageInfo_available() {
|
||||||
|
createMocks("test.provider", "test version 123")
|
||||||
|
assertThat(mainlineModuleVersionPreference.isAvailable(context)).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_versionIsNull() {
|
||||||
|
createMocks("test.provider", PackageInfo())
|
||||||
|
assertThat(mainlineModuleVersionPreference.getSummary(context)).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_versionIsEmpty() {
|
||||||
|
createMocks("test.provider", "")
|
||||||
|
assertThat(mainlineModuleVersionPreference.getSummary(context)).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_versionIsNotDate() {
|
||||||
|
createMocks("test.provider", "a")
|
||||||
|
assertThat(mainlineModuleVersionPreference.getSummary(context)).isEqualTo("a")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_versionIsMonth() {
|
||||||
|
createMocks("test.provider", "2019-05")
|
||||||
|
assertThat(mainlineModuleVersionPreference.getSummary(context)).isEqualTo("May 1, 2019")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_versionIsDate() {
|
||||||
|
createMocks("test.provider", "2019-05-13")
|
||||||
|
assertThat(mainlineModuleVersionPreference.getSummary(context)).isEqualTo("May 13, 2019")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnableFlags(Flags.FLAG_MAINLINE_MODULE_EXPLICIT_INTENT)
|
||||||
|
fun intentV2_preferenceShouldBeSelectable() {
|
||||||
|
intent_preferenceShouldBeSelectable(MODULE_UPDATE_ACTION_V2, MODULE_PACKAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisableFlags(Flags.FLAG_MAINLINE_MODULE_EXPLICIT_INTENT)
|
||||||
|
fun intent_preferenceShouldBeSelectable() {
|
||||||
|
intent_preferenceShouldBeSelectable(MODULE_UPDATE_ACTION, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun intent_preferenceShouldBeSelectable(action: String, intentPackage: String?) {
|
||||||
|
createMocks("test.provider", "test version 123") {
|
||||||
|
on { resolveActivity(any(), anyInt()) } doAnswer
|
||||||
|
{
|
||||||
|
when {
|
||||||
|
(it.arguments[0] as Intent).action == action -> ResolveInfo()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val preference = Preference(context)
|
||||||
|
mainlineModuleVersionPreference.bind(preference, mainlineModuleVersionPreference)
|
||||||
|
|
||||||
|
val intent = preference.intent!!
|
||||||
|
assertThat(intent.action).isEqualTo(action)
|
||||||
|
assertThat(preference.isSelectable).isTrue()
|
||||||
|
assertThat(intent.`package`).isEqualTo(intentPackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun intent_null() {
|
||||||
|
createMocks("test.provider", "test version 123")
|
||||||
|
|
||||||
|
val preference = Preference(context)
|
||||||
|
mainlineModuleVersionPreference.bind(preference, mainlineModuleVersionPreference)
|
||||||
|
|
||||||
|
assertThat(preference.intent).isNull()
|
||||||
|
assertThat(preference.isSelectable).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMocks(
|
||||||
|
pkg: String,
|
||||||
|
pkgInfo: Any?,
|
||||||
|
stubbing: KStubbing<PackageManager>.() -> Unit = {},
|
||||||
|
) {
|
||||||
|
mockResources = mock {
|
||||||
|
on { getString(R.string.config_mainline_module_update_package) } doReturn MODULE_PACKAGE
|
||||||
|
on {
|
||||||
|
getString(com.android.internal.R.string.config_defaultModuleMetadataProvider)
|
||||||
|
} doReturn pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
mockPackageManager = mock {
|
||||||
|
when (pkgInfo) {
|
||||||
|
is PackageInfo -> on { getPackageInfo(eq(pkg), anyInt()) } doReturn pkgInfo
|
||||||
|
is String ->
|
||||||
|
on { getPackageInfo(eq(pkg), anyInt()) } doReturn
|
||||||
|
PackageInfo().apply { versionName = pkgInfo }
|
||||||
|
is Exception -> on { getPackageInfo(eq(pkg), anyInt()) } doThrow pkgInfo
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
stubbing.invoke(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MODULE_PACKAGE = "com.android.vending"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LINT.ThenChange(MainlineModuleVersionPreferenceControllerTest.java)
|
Reference in New Issue
Block a user