Show current user first in KeyguardUserSwitcher
Update Adapter in KeyguardUserSwitcher so that the current user is
always the first entry in the list.
Bug: 169783558
Test: Open keyguard user switcher as secondary user, check that current
user is the first item in the list
Test: atest KeyguardUserSwitcherAdapterTest
Change-Id: I14ec36cbbfe908989f7a30e5c28f3b7badfb6ca2
This commit is contained in:
@@ -43,6 +43,8 @@ import com.android.systemui.qs.tiles.UserDetailItemView;
|
||||
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Manages the user switcher on the Keyguard.
|
||||
*/
|
||||
@@ -53,7 +55,7 @@ public class KeyguardUserSwitcher {
|
||||
|
||||
private final Container mUserSwitcherContainer;
|
||||
private final KeyguardStatusBarView mStatusBarView;
|
||||
private final Adapter mAdapter;
|
||||
private final KeyguardUserAdapter mAdapter;
|
||||
private final AppearAnimationUtils mAppearAnimationUtils;
|
||||
private final KeyguardUserSwitcherScrim mBackground;
|
||||
|
||||
@@ -76,7 +78,7 @@ public class KeyguardUserSwitcher {
|
||||
mStatusBarView = statusBarView;
|
||||
mStatusBarView.setKeyguardUserSwitcher(this);
|
||||
panelViewController.setKeyguardUserSwitcher(this);
|
||||
mAdapter = new Adapter(context, userSwitcherController, this);
|
||||
mAdapter = new KeyguardUserAdapter(context, userSwitcherController, this);
|
||||
mAdapter.registerDataSetObserver(mDataSetObserver);
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
|
||||
@@ -259,30 +261,70 @@ public class KeyguardUserSwitcher {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Adapter extends UserSwitcherController.BaseUserAdapter implements
|
||||
View.OnClickListener {
|
||||
static class KeyguardUserAdapter extends
|
||||
UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
|
||||
|
||||
private Context mContext;
|
||||
private KeyguardUserSwitcher mKeyguardUserSwitcher;
|
||||
private View mCurrentUserView;
|
||||
// List of users where the first entry is always the current user
|
||||
private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
|
||||
|
||||
public Adapter(Context context, UserSwitcherController controller,
|
||||
KeyguardUserAdapter(Context context, UserSwitcherController controller,
|
||||
KeyguardUserSwitcher kgu) {
|
||||
super(controller);
|
||||
mContext = context;
|
||||
mKeyguardUserSwitcher = kgu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
refreshUserOrder();
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
void refreshUserOrder() {
|
||||
ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
|
||||
mUsersOrdered = new ArrayList<>(users.size());
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
UserSwitcherController.UserRecord record = users.get(i);
|
||||
if (record.isCurrent) {
|
||||
mUsersOrdered.add(0, record);
|
||||
} else {
|
||||
mUsersOrdered.add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
|
||||
return mUsersOrdered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
UserSwitcherController.UserRecord item = getItem(position);
|
||||
if (!(convertView instanceof UserDetailItemView)
|
||||
return createUserDetailItemView(convertView, parent, item);
|
||||
}
|
||||
|
||||
KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
|
||||
if (!(convertView instanceof KeyguardUserDetailItemView)
|
||||
|| !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
|
||||
convertView = LayoutInflater.from(mContext).inflate(
|
||||
R.layout.keyguard_user_switcher_item, parent, false);
|
||||
convertView.setOnClickListener(this);
|
||||
}
|
||||
UserDetailItemView v = (UserDetailItemView) convertView;
|
||||
return (KeyguardUserDetailItemView) convertView;
|
||||
}
|
||||
|
||||
UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
|
||||
UserSwitcherController.UserRecord item) {
|
||||
KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
|
||||
if (!item.isCurrent || item.isGuest) {
|
||||
v.setOnClickListener(this);
|
||||
} else {
|
||||
v.setOnClickListener(null);
|
||||
v.setClickable(false);
|
||||
}
|
||||
|
||||
String name = getName(mContext, item);
|
||||
if (item.picture == null) {
|
||||
|
||||
@@ -605,19 +605,23 @@ public class UserSwitcherController implements Dumpable {
|
||||
controller.addAdapter(new WeakReference<>(this));
|
||||
}
|
||||
|
||||
protected ArrayList<UserRecord> getUsers() {
|
||||
return mController.getUsers();
|
||||
}
|
||||
|
||||
public int getUserCount() {
|
||||
boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
|
||||
&& mKeyguardStateController.isMethodSecure()
|
||||
&& !mKeyguardStateController.canDismissLockScreen();
|
||||
if (!secureKeyguardShowing) {
|
||||
return mController.getUsers().size();
|
||||
return getUsers().size();
|
||||
}
|
||||
// The lock screen is secure and showing. Filter out restricted records.
|
||||
final int N = mController.getUsers().size();
|
||||
final int userSize = getUsers().size();
|
||||
int count = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mController.getUsers().get(i).isGuest) continue;
|
||||
if (mController.getUsers().get(i).isRestricted) {
|
||||
for (int i = 0; i < userSize; i++) {
|
||||
if (getUsers().get(i).isGuest) continue;
|
||||
if (getUsers().get(i).isRestricted) {
|
||||
break;
|
||||
} else {
|
||||
count++;
|
||||
@@ -632,13 +636,13 @@ public class UserSwitcherController implements Dumpable {
|
||||
&& mKeyguardStateController.isMethodSecure()
|
||||
&& !mKeyguardStateController.canDismissLockScreen();
|
||||
if (!secureKeyguardShowing) {
|
||||
return mController.getUsers().size();
|
||||
return getUsers().size();
|
||||
}
|
||||
// The lock screen is secure and showing. Filter out restricted records.
|
||||
final int N = mController.getUsers().size();
|
||||
final int userSize = getUsers().size();
|
||||
int count = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mController.getUsers().get(i).isRestricted) {
|
||||
for (int i = 0; i < userSize; i++) {
|
||||
if (getUsers().get(i).isRestricted) {
|
||||
break;
|
||||
} else {
|
||||
count++;
|
||||
@@ -649,7 +653,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
|
||||
@Override
|
||||
public UserRecord getItem(int position) {
|
||||
return mController.getUsers().get(position);
|
||||
return getUsers().get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.systemui.statusbar.policy
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.UserInfo
|
||||
import android.graphics.Bitmap
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.util.UserIcons
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.qs.tiles.UserDetailItemView
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyBoolean
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var userSwitcherController: UserSwitcherController
|
||||
@Mock
|
||||
private lateinit var parent: ViewGroup
|
||||
@Mock
|
||||
private lateinit var keyguardUserDetailItemView: KeyguardUserDetailItemView
|
||||
@Mock
|
||||
private lateinit var otherView: View
|
||||
@Mock
|
||||
private lateinit var inflatedUserDetailItemView: KeyguardUserDetailItemView
|
||||
@Mock
|
||||
private lateinit var userInfo: UserInfo
|
||||
@Mock
|
||||
private lateinit var layoutInflater: LayoutInflater
|
||||
@Mock
|
||||
private lateinit var keyguardUserSwitcher: KeyguardUserSwitcher
|
||||
|
||||
private lateinit var adapter: KeyguardUserSwitcher.KeyguardUserAdapter
|
||||
private lateinit var picture: Bitmap
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater)
|
||||
`when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
|
||||
.thenReturn(inflatedUserDetailItemView)
|
||||
adapter = KeyguardUserSwitcher.KeyguardUserAdapter(mContext, userSwitcherController,
|
||||
keyguardUserSwitcher)
|
||||
picture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView has an
|
||||
* incompatible type
|
||||
*/
|
||||
private fun createViewFromDifferentType(
|
||||
isCurrentUser: Boolean,
|
||||
isGuestUser: Boolean
|
||||
): UserDetailItemView? {
|
||||
val user = createUserRecord(isCurrentUser, isGuestUser)
|
||||
return adapter.createUserDetailItemView(otherView, parent, user)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView is an
|
||||
* instance of KeyguardUserDetailItemView
|
||||
*/
|
||||
private fun createViewFromSameType(
|
||||
isCurrentUser: Boolean,
|
||||
isGuestUser: Boolean
|
||||
): UserDetailItemView? {
|
||||
val user = createUserRecord(isCurrentUser, isGuestUser)
|
||||
return adapter.createUserDetailItemView(keyguardUserDetailItemView, parent, user)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsSameType() {
|
||||
val v: UserDetailItemView? = createViewFromSameType(
|
||||
isCurrentUser = false, isGuestUser = false)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsSameType() {
|
||||
val v: UserDetailItemView? = createViewFromSameType(
|
||||
isCurrentUser = false, isGuestUser = true)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
|
||||
val v: UserDetailItemView? = createViewFromSameType(
|
||||
isCurrentUser = true, isGuestUser = false)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsSameType() {
|
||||
val v: UserDetailItemView? = createViewFromSameType(
|
||||
isCurrentUser = true, isGuestUser = true)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsDifferentType() {
|
||||
val v: UserDetailItemView? = createViewFromDifferentType(
|
||||
isCurrentUser = false, isGuestUser = false)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsDifferentType() {
|
||||
val v: UserDetailItemView? = createViewFromDifferentType(
|
||||
isCurrentUser = false, isGuestUser = true)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
|
||||
val v: UserDetailItemView? = createViewFromDifferentType(
|
||||
isCurrentUser = true, isGuestUser = false)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsDifferentType() {
|
||||
val v: UserDetailItemView? = createViewFromDifferentType(
|
||||
isCurrentUser = true, isGuestUser = true)
|
||||
assertNotNull(v)
|
||||
verify(v)!!.setOnClickListener(adapter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCurrentUserIsAlwaysFirst() {
|
||||
`when`(userSwitcherController.users).thenReturn(arrayListOf(
|
||||
createUserRecord(isCurrentUser = false, isGuestUser = false),
|
||||
createUserRecord(isCurrentUser = true, isGuestUser = false),
|
||||
createUserRecord(isCurrentUser = false, isGuestUser = true),
|
||||
createUserRecord(isCurrentUser = false, isGuestUser = false)
|
||||
))
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
assertTrue("Expected current user to be first in list", adapter.getItem(0).isCurrent)
|
||||
assertFalse("Did not expect current user in position 1", adapter.getItem(1).isCurrent)
|
||||
assertFalse("Did not expect current user in position 2", adapter.getItem(2).isCurrent)
|
||||
assertTrue("Expected guest user to remain in position 2", adapter.getItem(2).isGuest)
|
||||
assertFalse("Did not expect current user in position 3", adapter.getItem(3).isCurrent)
|
||||
}
|
||||
|
||||
private fun createUserRecord(isCurrentUser: Boolean, isGuestUser: Boolean) =
|
||||
UserSwitcherController.UserRecord(
|
||||
userInfo,
|
||||
picture,
|
||||
isGuestUser,
|
||||
isCurrentUser,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
}
|
||||
Reference in New Issue
Block a user