diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2c3e7f3d12a..a20c5e23a69 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3759,6 +3759,7 @@ diff --git a/res/values/config.xml b/res/values/config.xml index 0c5c7a05edf..5ae0220d29b 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -345,6 +345,9 @@ false false + + true diff --git a/src/com/android/settings/RegulatoryInfoDisplayActivity.java b/src/com/android/settings/RegulatoryInfoDisplayActivity.java deleted file mode 100644 index 8f650511a4d..00000000000 --- a/src/com/android/settings/RegulatoryInfoDisplayActivity.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2013 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; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.SystemProperties; -import android.text.TextUtils; -import android.view.Gravity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; - -import java.util.Locale; - -/** - * {@link Activity} that displays regulatory information for the "Regulatory information" - * preference item, and when "*#07#" is dialed on the Phone keypad. To enable this feature, - * set the "config_show_regulatory_info" boolean to true in a device overlay resource, and in the - * same overlay, either add a drawable named "regulatory_info.png" containing a graphical version - * of the required regulatory info (If ro.bootloader.hardware.sku property is set use - * "regulatory_info_.png where sku is ro.bootloader.hardware.sku property value in lowercase"), - * or add a string resource named "regulatory_info_text" with an HTML version of the required - * information (text will be centered in the dialog). - */ -public class RegulatoryInfoDisplayActivity extends Activity implements - DialogInterface.OnDismissListener { - - private final String REGULATORY_INFO_RESOURCE = "regulatory_info"; - private static final String DEFAULT_REGULATORY_INFO_FILEPATH = - "/data/misc/elabel/regulatory_info.png"; - private static final String REGULATORY_INFO_FILEPATH_TEMPLATE = - "/data/misc/elabel/regulatory_info_%s.png"; - - /** - * Display the regulatory info graphic in a dialog window. - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setTitle(R.string.regulatory_labels) - .setOnDismissListener(this) - .setPositiveButton(android.R.string.ok, null /* onClickListener */); - - boolean regulatoryInfoDrawableExists = false; - - final String regulatoryInfoFile = getRegulatoryInfoImageFileName(); - final Bitmap regulatoryInfoBitmap = BitmapFactory.decodeFile(regulatoryInfoFile); - - if (regulatoryInfoBitmap != null) { - regulatoryInfoDrawableExists = true; - } - - int resId = 0; - if (!regulatoryInfoDrawableExists) { - resId = getResourceId(); - } - if (resId != 0) { - try { - Drawable d = getDrawable(resId); - // set to false if the width or height is <= 2 - // (missing PNG can return an empty 2x2 pixel Drawable) - regulatoryInfoDrawableExists = (d.getIntrinsicWidth() > 2 - && d.getIntrinsicHeight() > 2); - } catch (Resources.NotFoundException ignored) { - regulatoryInfoDrawableExists = false; - } - } - - CharSequence regulatoryText = getResources() - .getText(R.string.regulatory_info_text); - - if (regulatoryInfoDrawableExists) { - View view = getLayoutInflater().inflate(R.layout.regulatory_info, null); - ImageView image = view.findViewById(R.id.regulatoryInfo); - if (regulatoryInfoBitmap != null) { - image.setImageBitmap(regulatoryInfoBitmap); - } else { - image.setImageResource(resId); - } - builder.setView(view); - builder.show(); - } else if (regulatoryText.length() > 0) { - builder.setMessage(regulatoryText); - AlertDialog dialog = builder.show(); - // we have to show the dialog first, or the setGravity() call will throw a NPE - TextView messageText = (TextView) dialog.findViewById(android.R.id.message); - messageText.setGravity(Gravity.CENTER); - } else { - // neither drawable nor text resource exists, finish activity - finish(); - } - } - - @VisibleForTesting - int getResourceId() { - // Use regulatory_info by default. - int resId = getResources().getIdentifier( - REGULATORY_INFO_RESOURCE, "drawable", getPackageName()); - - // When hardware sku property exists, use regulatory_info_ resource if valid. - final String sku = getSku(); - if (!TextUtils.isEmpty(sku)) { - String regulatory_info_res = REGULATORY_INFO_RESOURCE + "_" + sku.toLowerCase(); - int id = getResources().getIdentifier( - regulatory_info_res, "drawable", getPackageName()); - if (id != 0) { - resId = id; - } - } - - // When hardware coo property exists, use regulatory_info__ resource if valid. - final String coo = getCoo(); - if (!TextUtils.isEmpty(coo) && !TextUtils.isEmpty(sku)) { - final String regulatory_info_coo_res = - REGULATORY_INFO_RESOURCE + "_" + sku.toLowerCase() + "_" + coo.toLowerCase(); - final int id = getResources().getIdentifier( - regulatory_info_coo_res, "drawable", getPackageName()); - if (id != 0) { - resId = id; - } - } - return resId; - } - - @Override - public void onDismiss(DialogInterface dialog) { - finish(); // close the activity - } - - private String getCoo() { - return SystemProperties.get("ro.boot.hardware.coo", ""); - } - - private String getSku() { - return SystemProperties.get("ro.boot.hardware.sku", ""); - } - - private String getRegulatoryInfoImageFileName() { - final String sku = getSku(); - if (TextUtils.isEmpty(sku)) { - return DEFAULT_REGULATORY_INFO_FILEPATH; - } else { - return String.format(Locale.US, REGULATORY_INFO_FILEPATH_TEMPLATE, - sku.toLowerCase()); - } - } -} diff --git a/src/com/android/settings/RegulatoryInfoDisplayActivity.kt b/src/com/android/settings/RegulatoryInfoDisplayActivity.kt new file mode 100644 index 00000000000..ffacb9c192a --- /dev/null +++ b/src/com/android/settings/RegulatoryInfoDisplayActivity.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 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 + +import android.app.Activity +import android.os.Bundle +import android.view.Gravity +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.getRegulatoryInfo + +/** + * [Activity] that displays regulatory information for the "Regulatory information" + * preference item, and when "*#07#" is dialed on the Phone keypad. To enable this feature, + * set the "config_show_regulatory_info" boolean to true in a device overlay resource, and in the + * same overlay, either add a drawable named "regulatory_info.png" containing a graphical version + * of the required regulatory info (If ro.bootloader.hardware.sku property is set use + * "regulatory_info_.png where sku is ro.bootloader.hardware.sku property value in lowercase"), + * or add a string resource named "regulatory_info_text" with an HTML version of the required + * information (text will be centered in the dialog). + */ +class RegulatoryInfoDisplayActivity : Activity() { + + /** Display the regulatory info graphic in a dialog window. */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val builder = AlertDialog.Builder(this) + .setTitle(R.string.regulatory_labels) + .setOnDismissListener { finish() } // close the activity + .setPositiveButton(android.R.string.ok, null) + + getRegulatoryInfo()?.let { + val view = layoutInflater.inflate(R.layout.regulatory_info, null) + val image = view.findViewById(R.id.regulatoryInfo) + image.setImageDrawable(it) + builder.setView(view) + builder.show() + return + } + + val regulatoryText = resources.getText(R.string.regulatory_info_text) + if (regulatoryText.isNotEmpty()) { + builder.setMessage(regulatoryText) + val dialog = builder.show() + // we have to show the dialog first, or the setGravity() call will throw a NPE + dialog.findViewById(android.R.id.message)?.gravity = Gravity.CENTER + } else { + // neither drawable nor text resource exists, finish activity + finish() + } + } +} diff --git a/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt b/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt new file mode 100644 index 00000000000..e26e0610502 --- /dev/null +++ b/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 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.regulatory + +import android.content.Context +import android.content.res.Resources +import android.graphics.drawable.Drawable +import android.os.SystemProperties +import androidx.annotation.DrawableRes +import androidx.annotation.VisibleForTesting +import com.android.settings.R + + + +/** To load Regulatory Info from device. */ +object RegulatoryInfo { + private const val REGULATORY_INFO_RESOURCE = "regulatory_info" + + @VisibleForTesting + const val KEY_COO = "ro.boot.hardware.coo" + + @VisibleForTesting + const val KEY_SKU = "ro.boot.hardware.sku" + + /** Gets the regulatory drawable. */ + fun Context.getRegulatoryInfo(): Drawable? { + val sku = getSku() + if (sku.isNotBlank()) { + // When hardware coo property exists, use regulatory_info__ resource if valid. + val coo = getCoo() + if (coo.isNotBlank()) { + getRegulatoryInfo("${REGULATORY_INFO_RESOURCE}_${sku}_$coo")?.let { return it } + } + // Use regulatory_info_ resource if valid. + getRegulatoryInfo("${REGULATORY_INFO_RESOURCE}_$sku")?.let { return it } + } + return getRegulatoryInfo(REGULATORY_INFO_RESOURCE) + } + + private fun getCoo(): String = SystemProperties.get(KEY_COO).lowercase() + + private fun getSku(): String = SystemProperties.get(KEY_SKU).lowercase() + + private fun Context.getRegulatoryInfo(fileName: String): Drawable? { + val overlayPackageName = + resources.getString(R.string.config_regulatory_info_overlay_package_name) + .ifBlank { packageName } + val resources = packageManager.getResourcesForApplication(overlayPackageName) + val id = resources.getIdentifier(fileName, "drawable", overlayPackageName) + return if (id > 0) resources.getRegulatoryInfo(id) else null + } + + private fun Resources.getRegulatoryInfo(@DrawableRes resId: Int): Drawable? = try { + getDrawable(resId, null).takeIf { + // Ignore the placeholder image + it.intrinsicWidth > 10 && it.intrinsicHeight > 10 + } + } catch (_: Resources.NotFoundException) { + null + } +} diff --git a/tests/robotests/res/drawable/regulatory_info.png b/tests/robotests/res/drawable/regulatory_info.png deleted file mode 100644 index 65de26c0eb2..00000000000 Binary files a/tests/robotests/res/drawable/regulatory_info.png and /dev/null differ diff --git a/tests/robotests/res/drawable/regulatory_info_sku.png b/tests/robotests/res/drawable/regulatory_info_sku.png deleted file mode 100644 index 65de26c0eb2..00000000000 Binary files a/tests/robotests/res/drawable/regulatory_info_sku.png and /dev/null differ diff --git a/tests/robotests/res/drawable/regulatory_info_sku1_coo.png b/tests/robotests/res/drawable/regulatory_info_sku1_coo.png deleted file mode 100644 index 65de26c0eb2..00000000000 Binary files a/tests/robotests/res/drawable/regulatory_info_sku1_coo.png and /dev/null differ diff --git a/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java b/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java deleted file mode 100644 index d05d5d77d4a..00000000000 --- a/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2019 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; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.SystemProperties; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; - - -@RunWith(RobolectricTestRunner.class) -public class RegulatoryInfoDisplayActivityTest { - - private static final String SKU_PROP_KEY = "ro.boot.hardware.sku"; - private static final String COO_PROP_KEY = "ro.boot.hardware.coo"; - - private RegulatoryInfoDisplayActivity mRegulatoryInfoDisplayActivity; - - @Before - public void setUp() { - mRegulatoryInfoDisplayActivity = Robolectric.buildActivity( - RegulatoryInfoDisplayActivity.class).create().get(); - } - - @Test - public void getResourceId_noSkuProperty_shouldReturnDefaultLabel() { - SystemProperties.set(SKU_PROP_KEY, ""); - - final int expectedResId = getResourceId("regulatory_info"); - assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId); - } - - @Test - public void getResourceId_noCooProperty_shouldReturnSkuLabel() { - SystemProperties.set(SKU_PROP_KEY, "sku"); - SystemProperties.set(COO_PROP_KEY, ""); - - final int expectedResId = getResourceId("regulatory_info_sku"); - assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId); - } - - @Test - public void getResourceId_hasSkuAndCooProperties_shouldReturnCooLabel() { - SystemProperties.set(SKU_PROP_KEY, "sku1"); - SystemProperties.set(COO_PROP_KEY, "coo"); - - final int expectedResId = getResourceId("regulatory_info_sku1_coo"); - assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId); - } - - @Test - public void getResourceId_noCorrespondingCooLabel_shouldReturnSkuLabel() { - SystemProperties.set(SKU_PROP_KEY, "sku"); - SystemProperties.set(COO_PROP_KEY, "unknown"); - - final int expectedResId = getResourceId("regulatory_info_sku"); - assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId); - } - - private int getResourceId(String resourceName) { - return mRegulatoryInfoDisplayActivity.getResources().getIdentifier(resourceName, "drawable", - mRegulatoryInfoDisplayActivity.getPackageName()); - } -} diff --git a/tests/spa_unit/res/drawable/regulatory_info.xml b/tests/spa_unit/res/drawable/regulatory_info.xml new file mode 100644 index 00000000000..7cc3803e6db --- /dev/null +++ b/tests/spa_unit/res/drawable/regulatory_info.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/tests/spa_unit/res/drawable/regulatory_info_sku.xml b/tests/spa_unit/res/drawable/regulatory_info_sku.xml new file mode 100644 index 00000000000..634e55edafc --- /dev/null +++ b/tests/spa_unit/res/drawable/regulatory_info_sku.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml b/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml new file mode 100644 index 00000000000..7e6b9efc721 --- /dev/null +++ b/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt new file mode 100644 index 00000000000..f1e18fc8023 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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.regulatory + +import android.content.Context +import android.graphics.drawable.Drawable +import android.os.SystemProperties +import androidx.annotation.DrawableRes +import androidx.core.graphics.drawable.toBitmap +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_COO +import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_SKU +import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.getRegulatoryInfo +import com.android.settings.tests.spa_unit.R +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoSession +import org.mockito.Spy +import org.mockito.quality.Strictness + +@RunWith(AndroidJUnit4::class) +class RegulatoryInfoTest { + private lateinit var mockSession: MockitoSession + + @Spy + private val context: Context = ApplicationProvider.getApplicationContext() + + @Before + fun setUp() { + mockSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .mockStatic(SystemProperties::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + } + + @After + fun tearDown() { + mockSession.finishMocking() + } + + @Test + fun getRegulatoryInfo_noSkuProperty_shouldReturnDefaultLabel() { + doReturn("").`when` { SystemProperties.get(KEY_SKU) } + + val regulatoryInfo = context.getRegulatoryInfo() + + assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info) + } + + @Test + fun getResourceId_noCooProperty_shouldReturnSkuLabel() { + doReturn("sku").`when` { SystemProperties.get(KEY_SKU) } + doReturn("").`when` { SystemProperties.get(KEY_COO) } + + val regulatoryInfo = context.getRegulatoryInfo() + + assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku) + } + + @Test + fun getResourceId_hasSkuAndCooProperties_shouldReturnCooLabel() { + doReturn("sku1").`when` { SystemProperties.get(KEY_SKU) } + doReturn("coo").`when` { SystemProperties.get(KEY_COO) } + + val regulatoryInfo = context.getRegulatoryInfo() + + assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku1_coo) + } + + @Test + fun getResourceId_noCorrespondingCooLabel_shouldReturnSkuLabel() { + doReturn("sku").`when` { SystemProperties.get(KEY_SKU) } + doReturn("unknown").`when` { SystemProperties.get(KEY_COO) } + + val regulatoryInfo = context.getRegulatoryInfo() + + assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku) + } + + private fun assertDrawableSameAs(drawable: Drawable?, @DrawableRes resId: Int) { + val expected = context.getDrawable(resId)!!.toBitmap() + assertThat(drawable!!.toBitmap().sameAs(expected)).isTrue() + } +}