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
This commit is contained in:
Peter Kalauskas
2022-04-05 16:34:15 -07:00
parent dd7c5737a4
commit 5bf13fa999
4 changed files with 112 additions and 36 deletions

View File

@@ -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) {

View File

@@ -137,4 +137,8 @@ public class UserAvatarView extends View {
mDrawable.setIconDrawable(d);
mDrawable.setBadgeIfManagedUser(getContext(), userId);
}
public boolean isEmpty() {
return mDrawable.isEmpty();
}
}

View File

@@ -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<FrameLayout>
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<FrameLayout>
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<FrameLayout>
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<FrameLayout>
}
};
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>
FrameLayout view,
Context context,
@Main Resources resources,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
KeyguardStateController keyguardStateController,
FalsingManager falsingManager,
@@ -130,7 +150,6 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
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<FrameLayout>
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<FrameLayout>
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<FrameLayout>
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);
}

View File

@@ -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()
}
}