From 1c4eb9dc759097ce61c8c323eb5cefdde39a220a Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 15 Jun 2023 08:48:47 +0000 Subject: [PATCH 1/2] Set the light status bar flag if not in dark mode To make the status bar content clearly visible we should set the light status bar flag if the device is not in dark mode. Bug: 241274551 Test: 1. Add an account and setup fingerprint unlock 2. Go to "Settings>Passwords & accounts>add account" to add another account 3. Wait for BiometricPrompt to pop up and check the status bar. Change-Id: I7208b9c18c3734d150dfaaef6724948bd197066a --- .../password/ConfirmDeviceCredentialActivity.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index 314ce053127..f2f6520c361 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -23,6 +23,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_W import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PASSWORD; import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN; import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import android.app.Activity; import android.app.KeyguardManager; @@ -32,6 +33,7 @@ import android.app.trust.TrustManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.graphics.Color; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; @@ -379,6 +381,12 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { // Translucent activity that is "visible", so it doesn't complain about finish() // not being called before onResume(). setVisible(true); + + if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) + != Configuration.UI_MODE_NIGHT_YES) { + getWindow().getInsetsController().setSystemBarsAppearance( + APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); + } } @Override From 88fd45b1e6d523acaa7a43d635e57b7cc9a7ef7f Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 21 Jun 2023 16:34:07 +0800 Subject: [PATCH 2/2] Fix ANR in TelephonyStatusControlSession Feature.get() blocks on the main thread, which cause the ANR. Cancel the job instead to fix. Fix: 287702163 Test: Manually with MobileNetworkSettings Test: atest TelephonyStatusControlSessionTest Change-Id: Id873e56359dbf198c31686c2280c979294c95c3d --- .../AbstractMobileNetworkSettings.java | 4 +- .../TelephonyStatusControlSession.java | 117 ------------------ .../TelephonyStatusControlSession.kt | 86 +++++++++++++ .../TelephonyStatusControlSessionTest.kt | 81 ++++++++++++ 4 files changed, 168 insertions(+), 120 deletions(-) delete mode 100644 src/com/android/settings/network/telephony/TelephonyStatusControlSession.java create mode 100644 src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java index 245ac8357ed..7addb597372 100644 --- a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java @@ -18,7 +18,6 @@ package com.android.settings.network.telephony; import android.os.SystemClock; import android.text.TextUtils; -import android.util.Log; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -66,8 +65,7 @@ abstract class AbstractMobileNetworkSettings extends RestrictedDashboardFragment TelephonyStatusControlSession setTelephonyAvailabilityStatus( Collection listOfPrefControllers) { - return (new TelephonyStatusControlSession.Builder(listOfPrefControllers)) - .build(); + return new TelephonyStatusControlSession(listOfPrefControllers, getLifecycle()); } @Override diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java deleted file mode 100644 index 3716f1f864e..00000000000 --- a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2020 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.network.telephony; - -import android.util.Log; - -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.utils.ThreadUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -/** - * Session for controlling the status of TelephonyPreferenceController(s). - * - * Within this session, result of {@link BasePreferenceController#availabilityStatus()} - * would be under control. - */ -public class TelephonyStatusControlSession implements AutoCloseable { - - private static final String LOG_TAG = "TelephonyStatusControlSS"; - - private Collection mControllers; - private Collection> mResult = new ArrayList<>(); - - /** - * Buider of session - */ - public static class Builder { - private Collection mControllers; - - /** - * Constructor - * - * @param controllers is a collection of {@link AbstractPreferenceController} - * which would have {@link BasePreferenceController#availabilityStatus()} - * under control within this session. - */ - public Builder(Collection controllers) { - mControllers = controllers; - } - - /** - * Method to build this session. - * @return {@link TelephonyStatusControlSession} session been setup. - */ - public TelephonyStatusControlSession build() { - return new TelephonyStatusControlSession(mControllers); - } - } - - private TelephonyStatusControlSession(Collection controllers) { - mControllers = controllers; - controllers.forEach(prefCtrl -> mResult - .add(ThreadUtils.postOnBackgroundThread(() -> setupAvailabilityStatus(prefCtrl)))); - - } - - /** - * Close the session. - * - * No longer control the status. - */ - public void close() { - //check the background thread is finished then unset the status of availability. - - for (Future result : mResult) { - try { - result.get(); - } catch (ExecutionException | InterruptedException exception) { - Log.e(LOG_TAG, "setup availability status failed!", exception); - } - } - unsetAvailabilityStatus(mControllers); - } - - private Boolean setupAvailabilityStatus(AbstractPreferenceController controller) { - try { - if (controller instanceof TelephonyAvailabilityHandler) { - int status = ((BasePreferenceController) controller) - .getAvailabilityStatus(); - ((TelephonyAvailabilityHandler) controller).setAvailabilityStatus(status); - } - return true; - } catch (Exception exception) { - Log.e(LOG_TAG, "Setup availability status failed!", exception); - return false; - } - } - - private void unsetAvailabilityStatus( - Collection controllerLists) { - controllerLists.stream() - .filter(controller -> controller instanceof TelephonyAvailabilityHandler) - .map(TelephonyAvailabilityHandler.class::cast) - .forEach(controller -> { - controller.unsetAvailabilityStatus(); - }); - } -} diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt new file mode 100644 index 00000000000..0e63c8cce4e --- /dev/null +++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt @@ -0,0 +1,86 @@ +/* + * 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.network.telephony + +import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.coroutineScope +import com.android.settings.core.BasePreferenceController +import com.android.settingslib.core.AbstractPreferenceController +import com.google.common.collect.Sets +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.yield + +/** + * Session for controlling the status of TelephonyPreferenceController(s). + * + * Within this session, result of [BasePreferenceController.getAvailabilityStatus] + * would be under control. + */ +class TelephonyStatusControlSession( + private val controllers: Collection, + lifecycle: Lifecycle, +) : AutoCloseable { + private var job: Job? = null + private val controllerSet = Sets.newConcurrentHashSet() + + init { + job = lifecycle.coroutineScope.launch(Dispatchers.Default) { + for (controller in controllers) { + launch { + setupAvailabilityStatus(controller) + } + } + } + } + + /** + * Close the session. + * + * No longer control the status. + */ + override fun close() { + job?.cancel() + unsetAvailabilityStatus() + } + + private suspend fun setupAvailabilityStatus(controller: AbstractPreferenceController): Boolean = + try { + if (controller is TelephonyAvailabilityHandler) { + val status = (controller as BasePreferenceController).availabilityStatus + yield() // prompt cancellation guarantee + if (controllerSet.add(controller)) { + controller.setAvailabilityStatus(status) + } + } + true + } catch (exception: Exception) { + Log.e(LOG_TAG, "Setup availability status failed!", exception) + false + } + + private fun unsetAvailabilityStatus() { + for (controller in controllerSet) { + controller.unsetAvailabilityStatus() + } + } + + companion object { + private const val LOG_TAG = "TelephonyStatusControlSS" + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt new file mode 100644 index 00000000000..7e6a91b8c65 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt @@ -0,0 +1,81 @@ +/* + * 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.network.telephony + +import android.content.Context +import androidx.lifecycle.testing.TestLifecycleOwner +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.core.BasePreferenceController +import com.android.settingslib.spa.testutils.waitUntil +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class TelephonyStatusControlSessionTest { + private val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun init() = runTest { + val controller = TestController(context) + + val session = TelephonyStatusControlSession( + controllers = listOf(controller), + lifecycle = TestLifecycleOwner().lifecycle, + ) + + waitUntil { controller.availabilityStatus == STATUS } + session.close() + } + + @Test + fun close() = runTest { + val controller = TestController(context) + + val session = TelephonyStatusControlSession( + controllers = listOf(controller), + lifecycle = TestLifecycleOwner().lifecycle, + ) + session.close() + + assertThat(controller.availabilityStatus).isNull() + } + + private companion object { + const val KEY = "key" + const val STATUS = BasePreferenceController.AVAILABLE + } + + private class TestController(context: Context) : BasePreferenceController(context, KEY), + TelephonyAvailabilityHandler { + + var availabilityStatus: Int? = null + override fun getAvailabilityStatus(): Int = STATUS + + override fun setAvailabilityStatus(status: Int) { + availabilityStatus = status + } + + override fun unsetAvailabilityStatus() { + availabilityStatus = null + } + } +}