From 5bf13fa999a662b3a48e5bb830ccd1d7e4b3b1b6 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Tue, 5 Apr 2022 16:34:15 -0700 Subject: Remove user icon avatar from memory when unlocked Previously, the bitmap for the user icon would stay in memory even when the phone was unlocked. Now, the bitmap is cleared whenever the phone is unlocked to save memory. This change only affects the user-icon shown on the lockscreen next to the clock. Bug: 202868702 Test: KeyguardQsUserSwitchControllerTest Change-Id: Iec6a57adb65181fcef65489c248db77930f566bb --- .../settingslib/drawable/UserIconDrawable.java | 4 + .../systemui/statusbar/phone/UserAvatarView.java | 4 + .../policy/KeyguardQsUserSwitchController.java | 108 +++++++++++++++------ .../policy/KeyguardQsUserSwitchControllerTest.kt | 32 +++++- 4 files changed, 112 insertions(+), 36 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java index 94d90a8eb028..91d7388bc46d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java @@ -176,6 +176,10 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback { return this; } + public boolean isEmpty() { + return getUserIcon() == null && getUserDrawable() == null; + } + public UserIconDrawable setBadge(Drawable badge) { mBadge = badge; if (mBadge != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java index c404cf69f1a8..1b93c16dd45c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java @@ -137,4 +137,8 @@ public class UserAvatarView extends View { mDrawable.setIconDrawable(d); mDrawable.setBadgeIfManagedUser(getContext(), userId); } + + public boolean isEmpty() { + return mDrawable.isEmpty(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index e8bf89a6a90a..2a9048a6eb73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -29,6 +29,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardVisibilityHelper; @@ -36,7 +37,6 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherScope; import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.user.UserSwitchDialogController; @@ -68,7 +68,6 @@ public class KeyguardQsUserSwitchController extends ViewController private final Context mContext; private Resources mResources; private final UserSwitcherController mUserSwitcherController; - private final ScreenLifecycle mScreenLifecycle; private UserSwitcherController.BaseUserAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; private final FalsingManager mFalsingManager; @@ -77,8 +76,10 @@ public class KeyguardQsUserSwitchController extends ViewController private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; private final UserSwitchDialogController mUserSwitchDialogController; private final UiEventLogger mUiEventLogger; - private UserAvatarView mUserAvatarView; + @VisibleForTesting + UserAvatarView mUserAvatarView; UserSwitcherController.UserRecord mCurrentUser; + private boolean mIsKeyguardShowing; // State info for the user switch and keyguard private int mBarState; @@ -87,8 +88,6 @@ public class KeyguardQsUserSwitchController extends ViewController new StatusBarStateController.StateListener() { @Override public void onStateChanged(int newState) { - if (DEBUG) Log.d(TAG, String.format("onStateChanged: newState=%d", newState)); - boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); int oldState = mBarState; @@ -102,12 +101,34 @@ public class KeyguardQsUserSwitchController extends ViewController } }; - private ConfigurationController.ConfigurationListener - mConfigurationListener = new ConfigurationController.ConfigurationListener() { + private ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { @Override public void onUiModeChanged() { - updateView(true); + // Force update when dark theme toggled. Otherwise, icon will not update + // until it is clicked + if (mIsKeyguardShowing) { + updateView(); + } + } + }; + + private final KeyguardStateController.Callback mKeyguardStateCallback = + new KeyguardStateController.Callback() { + @Override + public void onUnlockedChanged() { + updateKeyguardShowing(false /* forceViewUpdate */); + } + + @Override + public void onKeyguardShowingChanged() { + updateKeyguardShowing(false /* forceViewUpdate */); + } + + @Override + public void onKeyguardFadingAwayChanged() { + updateKeyguardShowing(false /* forceViewUpdate */); } }; @@ -116,7 +137,6 @@ public class KeyguardQsUserSwitchController extends ViewController FrameLayout view, Context context, @Main Resources resources, - ScreenLifecycle screenLifecycle, UserSwitcherController userSwitcherController, KeyguardStateController keyguardStateController, FalsingManager falsingManager, @@ -130,7 +150,6 @@ public class KeyguardQsUserSwitchController extends ViewController if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController"); mContext = context; mResources = resources; - mScreenLifecycle = screenLifecycle; mUserSwitcherController = userSwitcherController; mKeyguardStateController = keyguardStateController; mFalsingManager = falsingManager; @@ -188,7 +207,10 @@ public class KeyguardQsUserSwitchController extends ViewController mDataSetObserver.onChanged(); mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); - updateView(true /* forceUpdate */); + mKeyguardStateController.addCallback(mKeyguardStateCallback); + // Force update when view attached in case configuration changed while the view was detached + updateCurrentUser(); + updateKeyguardShowing(true /* forceViewUpdate */); } @Override @@ -198,15 +220,48 @@ public class KeyguardQsUserSwitchController extends ViewController mAdapter.unregisterDataSetObserver(mDataSetObserver); mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); + mKeyguardStateController.removeCallback(mKeyguardStateCallback); } public final DataSetObserver mDataSetObserver = new DataSetObserver() { @Override public void onChanged() { - updateView(false /* forceUpdate */); + boolean userChanged = updateCurrentUser(); + if (userChanged || (mIsKeyguardShowing && mUserAvatarView.isEmpty())) { + updateView(); + } } }; + private void clearAvatar() { + if (DEBUG) Log.d(TAG, "clearAvatar"); + mUserAvatarView.setAvatar(null); + } + + /** + * @param forceViewUpdate whether view should be updated regardless of whether + * keyguard-showing state changed + */ + @VisibleForTesting + void updateKeyguardShowing(boolean forceViewUpdate) { + boolean wasKeyguardShowing = mIsKeyguardShowing; + mIsKeyguardShowing = mKeyguardStateController.isShowing() + || mKeyguardStateController.isKeyguardGoingAway(); + if (wasKeyguardShowing == mIsKeyguardShowing && !forceViewUpdate) { + return; + } + if (DEBUG) { + Log.d(TAG, "updateKeyguardShowing:" + + " mIsKeyguardShowing=" + mIsKeyguardShowing + + " forceViewUpdate=" + forceViewUpdate); + } + if (mIsKeyguardShowing) { + updateView(); + } else { + clearAvatar(); + } + } + /** * @return true if the current user has changed */ @@ -223,31 +278,22 @@ public class KeyguardQsUserSwitchController extends ViewController return mCurrentUser == null && previousUser != null; } - /** - * @param forceUpdate whether to update view even if current user did not change - */ - private void updateView(boolean forceUpdate) { - if (!updateCurrentUser() && !forceUpdate) { - return; - } - - String contentDescription = null; - if (mCurrentUser != null && mCurrentUser.info != null && !TextUtils.isEmpty( - mCurrentUser.info.name)) { + private String getContentDescription() { + if (mCurrentUser != null && mCurrentUser.info != null + && !TextUtils.isEmpty(mCurrentUser.info.name)) { // If we know the current user's name, have TalkBack to announce "Signed in as [user // name]" when the icon is selected - contentDescription = mContext.getString(R.string.accessibility_quick_settings_user, - mCurrentUser.info.name); + return mContext.getString( + R.string.accessibility_quick_settings_user, mCurrentUser.info.name); } else { // As a fallback, have TalkBack announce "Switch user" - contentDescription = mContext.getString( - R.string.accessibility_multi_user_switch_switcher); - } - - if (!TextUtils.equals(mUserAvatarView.getContentDescription(), contentDescription)) { - mUserAvatarView.setContentDescription(contentDescription); + return mContext.getString(R.string.accessibility_multi_user_switch_switcher); } + } + private void updateView() { + if (DEBUG) Log.d(TAG, "updateView"); + mUserAvatarView.setContentDescription(getContentDescription()); int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL; mUserAvatarView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt index f58403d3651a..b4f3987b2f95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt @@ -26,13 +26,13 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.LockscreenGestureLogger import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before import org.junit.Test @@ -47,9 +47,6 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner::class) class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { - @Mock - private lateinit var screenLifecycle: ScreenLifecycle - @Mock private lateinit var userSwitcherController: UserSwitcherController @@ -93,7 +90,6 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { view, context, context.resources, - screenLifecycle, userSwitcherController, keyguardStateController, falsingManager, @@ -108,6 +104,8 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { testableLooper.processAllMessages() `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController) `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true) + `when`(keyguardStateController.isShowing).thenReturn(true) + `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false) keyguardQsUserSwitchController.init() } @@ -122,4 +120,28 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { verify(uiEventLogger, times(1)) .log(LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP) } + + @Test + fun testAvatarExistsWhenKeyguardGoingAway() { + `when`(keyguardStateController.isShowing).thenReturn(false) + `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(true) + keyguardQsUserSwitchController.updateKeyguardShowing(true /* forceViewUpdate */) + assertThat(keyguardQsUserSwitchController.mUserAvatarView.isEmpty).isFalse() + } + + @Test + fun testAvatarExistsWhenKeyguardShown() { + `when`(keyguardStateController.isShowing).thenReturn(true) + `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false) + keyguardQsUserSwitchController.updateKeyguardShowing(true /* forceViewUpdate */) + assertThat(keyguardQsUserSwitchController.mUserAvatarView.isEmpty).isFalse() + } + + @Test + fun testAvatarGoneWhenKeyguardGone() { + `when`(keyguardStateController.isShowing).thenReturn(false) + `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false) + keyguardQsUserSwitchController.updateKeyguardShowing(true /* forceViewUpdate */) + assertThat(keyguardQsUserSwitchController.mUserAvatarView.isEmpty).isTrue() + } } -- cgit v1.2.3-59-g8ed1b