Merge changes from topic "mpietal_supervised_user"
* changes: User switcher - Manage users Add supervised user support
This commit is contained in:
24
packages/SystemUI/res/drawable/ic_account_circle.xml
Normal file
24
packages/SystemUI/res/drawable/ic_account_circle.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M5.85,17.1Q7.125,16.125 8.7,15.562Q10.275,15 12,15Q13.725,15 15.3,15.562Q16.875,16.125 18.15,17.1Q19.025,16.075 19.513,14.775Q20,13.475 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,13.475 4.488,14.775Q4.975,16.075 5.85,17.1ZM12,13Q10.525,13 9.512,11.988Q8.5,10.975 8.5,9.5Q8.5,8.025 9.512,7.012Q10.525,6 12,6Q13.475,6 14.488,7.012Q15.5,8.025 15.5,9.5Q15.5,10.975 14.488,11.988Q13.475,13 12,13ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,20Q13.325,20 14.5,19.613Q15.675,19.225 16.65,18.5Q15.675,17.775 14.5,17.387Q13.325,17 12,17Q10.675,17 9.5,17.387Q8.325,17.775 7.35,18.5Q8.325,19.225 9.5,19.613Q10.675,20 12,20ZM12,11Q12.65,11 13.075,10.575Q13.5,10.15 13.5,9.5Q13.5,8.85 13.075,8.425Q12.65,8 12,8Q11.35,8 10.925,8.425Q10.5,8.85 10.5,9.5Q10.5,10.15 10.925,10.575Q11.35,11 12,11ZM12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5Q12,9.5 12,9.5ZM12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Q12,18.5 12,18.5Z"/>
|
||||
</vector>
|
||||
27
packages/SystemUI/res/drawable/ic_account_circle_filled.xml
Normal file
27
packages/SystemUI/res/drawable/ic_account_circle_filled.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33A7.95,7.95 0,0 1,4 12c0,-4.41 3.59,-8 8,-8s8,3.59 8,8c0,1.82 -0.62,3.49 -1.64,4.83z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13s3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/>
|
||||
</vector>
|
||||
19
packages/SystemUI/res/drawable/ic_add_supervised_user.xml
Normal file
19
packages/SystemUI/res/drawable/ic_add_supervised_user.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<layer-list
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:drawable="@*android:drawable/ic_add_supervised_user" />
|
||||
</layer-list>
|
||||
23
packages/SystemUI/res/drawable/ic_manage_users.xml
Normal file
23
packages/SystemUI/res/drawable/ic_manage_users.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white"
|
||||
android:pathData="M10,12Q8.35,12 7.175,10.825Q6,9.65 6,8Q6,6.35 7.175,5.175Q8.35,4 10,4Q11.65,4 12.825,5.175Q14,6.35 14,8Q14,9.65 12.825,10.825Q11.65,12 10,12ZM2,20V17.2Q2,16.375 2.425,15.65Q2.85,14.925 3.6,14.55Q4.875,13.9 6.475,13.45Q8.075,13 10,13Q10.2,13 10.35,13Q10.5,13 10.65,13.05Q10.45,13.5 10.312,13.988Q10.175,14.475 10.1,15H10Q8.225,15 6.812,15.45Q5.4,15.9 4.5,16.35Q4.275,16.475 4.138,16.7Q4,16.925 4,17.2V18H10.3Q10.45,18.525 10.7,19.038Q10.95,19.55 11.25,20ZM16,21L15.7,19.5Q15.4,19.375 15.137,19.238Q14.875,19.1 14.6,18.9L13.15,19.35L12.15,17.65L13.3,16.65Q13.25,16.3 13.25,16Q13.25,15.7 13.3,15.35L12.15,14.35L13.15,12.65L14.6,13.1Q14.875,12.9 15.137,12.762Q15.4,12.625 15.7,12.5L16,11H18L18.3,12.5Q18.6,12.625 18.863,12.775Q19.125,12.925 19.4,13.15L20.85,12.65L21.85,14.4L20.7,15.4Q20.75,15.7 20.75,16.025Q20.75,16.35 20.7,16.65L21.85,17.65L20.85,19.35L19.4,18.9Q19.125,19.1 18.863,19.238Q18.6,19.375 18.3,19.5L18,21ZM17,18Q17.825,18 18.413,17.413Q19,16.825 19,16Q19,15.175 18.413,14.587Q17.825,14 17,14Q16.175,14 15.588,14.587Q15,15.175 15,16Q15,16.825 15.588,17.413Q16.175,18 17,18ZM10,10Q10.825,10 11.413,9.412Q12,8.825 12,8Q12,7.175 11.413,6.588Q10.825,6 10,6Q9.175,6 8.588,6.588Q8,7.175 8,8Q8,8.825 8.588,9.412Q9.175,10 10,10ZM10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8Q10,8 10,8ZM10,15Q10,15 10,15Q10,15 10,15Q10,15 10,15Q10,15 10,15Q10,15 10,15Q10,15 10,15Z"/>
|
||||
</vector>
|
||||
@@ -21,6 +21,7 @@
|
||||
android:id="@+id/user_switcher_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="64dp"
|
||||
android:layout_marginEnd="60dp"
|
||||
android:layout_marginStart="60dp">
|
||||
|
||||
|
||||
@@ -29,17 +29,19 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:scaleType="centerInside"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:contentDescription="@null"
|
||||
android:tint="@color/user_switcher_fullscreen_popup_item_tint"
|
||||
android:layout_marginEnd="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@*android:color/text_color_primary_device_default_dark"
|
||||
android:textColor="@color/user_switcher_fullscreen_popup_item_tint"
|
||||
android:textSize="14sp"
|
||||
android:layout_gravity="start" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
|
||||
<color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
|
||||
<color name="user_switcher_fullscreen_bg">@android:color/system_neutral1_900</color>
|
||||
<color name="user_switcher_fullscreen_popup_item_tint">@*android:color/text_color_primary_device_default_dark</color>
|
||||
|
||||
<!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
|
||||
<color name="navigation_bar_icon_color">#E5FFFFFF</color>
|
||||
|
||||
@@ -2402,4 +2402,8 @@
|
||||
|
||||
<!-- Generic "add" string [CHAR LIMIT=NONE] -->
|
||||
<string name="add">Add</string>
|
||||
<!-- Add supervised user -->
|
||||
<string name="add_user_supervised" translatable="false">@*android:string/supervised_user_creation_label</string>
|
||||
<!-- Manage users - For system user management [CHAR LIMIT=40] -->
|
||||
<string name="manage_users">Manage users</string>
|
||||
</resources>
|
||||
|
||||
@@ -46,6 +46,7 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyCallback;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
@@ -159,6 +160,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
private final AtomicBoolean mGuestCreationScheduled;
|
||||
private FalsingManager mFalsingManager;
|
||||
private View mView;
|
||||
private String mCreateSupervisedUserPackage;
|
||||
|
||||
@Inject
|
||||
public UserSwitcherController(Context context,
|
||||
@@ -255,6 +257,9 @@ public class UserSwitcherController implements Dumpable {
|
||||
keyguardStateController.addCallback(mCallback);
|
||||
listenForCallState();
|
||||
|
||||
mCreateSupervisedUserPackage = mContext.getString(
|
||||
com.android.internal.R.string.config_supervisedUserCreationPackage);
|
||||
|
||||
dumpManager.registerDumpable(getClass().getSimpleName(), this);
|
||||
|
||||
refreshUsers(UserHandle.USER_NULL);
|
||||
@@ -307,14 +312,10 @@ public class UserSwitcherController implements Dumpable {
|
||||
// User 0
|
||||
boolean canSwitchUsers = mUserManager.getUserSwitchability(
|
||||
UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
|
||||
UserInfo currentUserInfo = null;
|
||||
UserRecord guestRecord = null;
|
||||
|
||||
for (UserInfo info : infos) {
|
||||
boolean isCurrent = currentId == info.id;
|
||||
if (isCurrent) {
|
||||
currentUserInfo = info;
|
||||
}
|
||||
boolean switchToEnabled = canSwitchUsers || isCurrent;
|
||||
if (info.isEnabled()) {
|
||||
if (info.isGuest()) {
|
||||
@@ -322,7 +323,8 @@ public class UserSwitcherController implements Dumpable {
|
||||
// the icon shouldn't be enabled even if the user is current
|
||||
guestRecord = new UserRecord(info, null /* picture */,
|
||||
true /* isGuest */, isCurrent, false /* isAddUser */,
|
||||
false /* isRestricted */, canSwitchUsers);
|
||||
false /* isRestricted */, canSwitchUsers,
|
||||
false /* isAddSupervisedUser */);
|
||||
} else if (info.supportsSwitchToByUser()) {
|
||||
Bitmap picture = bitmaps.get(info.id);
|
||||
if (picture == null) {
|
||||
@@ -337,7 +339,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
}
|
||||
records.add(new UserRecord(info, picture, false /* isGuest */,
|
||||
isCurrent, false /* isAddUser */, false /* isRestricted */,
|
||||
switchToEnabled));
|
||||
switchToEnabled, false /* isAddSupervisedUser */));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,19 +347,6 @@ public class UserSwitcherController implements Dumpable {
|
||||
Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
|
||||
}
|
||||
|
||||
boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
|
||||
UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
|
||||
boolean currentUserCanCreateUsers = currentUserInfo != null
|
||||
&& (currentUserInfo.isAdmin()
|
||||
|| currentUserInfo.id == UserHandle.USER_SYSTEM)
|
||||
&& systemCanCreateUsers;
|
||||
boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
|
||||
boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
|
||||
&& guestRecord == null;
|
||||
boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
|
||||
&& mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
|
||||
boolean createIsRestricted = !addUsersWhenLocked;
|
||||
|
||||
if (guestRecord == null) {
|
||||
if (mGuestUserAutoCreated) {
|
||||
// If mGuestIsResetting=true, the switch should be disabled since
|
||||
@@ -368,13 +357,14 @@ public class UserSwitcherController implements Dumpable {
|
||||
guestRecord = new UserRecord(null /* info */, null /* picture */,
|
||||
true /* isGuest */, false /* isCurrent */,
|
||||
false /* isAddUser */, false /* isRestricted */,
|
||||
isSwitchToGuestEnabled);
|
||||
isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
|
||||
checkIfAddUserDisallowedByAdminOnly(guestRecord);
|
||||
records.add(guestRecord);
|
||||
} else if (canCreateGuest) {
|
||||
} else if (canCreateGuest(guestRecord != null)) {
|
||||
guestRecord = new UserRecord(null /* info */, null /* picture */,
|
||||
true /* isGuest */, false /* isCurrent */,
|
||||
false /* isAddUser */, createIsRestricted, canSwitchUsers);
|
||||
false /* isAddUser */, createIsRestricted(), canSwitchUsers,
|
||||
false /* isAddSupervisedUser */);
|
||||
checkIfAddUserDisallowedByAdminOnly(guestRecord);
|
||||
records.add(guestRecord);
|
||||
}
|
||||
@@ -382,10 +372,19 @@ public class UserSwitcherController implements Dumpable {
|
||||
records.add(guestRecord);
|
||||
}
|
||||
|
||||
if (canCreateUser) {
|
||||
if (canCreateUser()) {
|
||||
UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
|
||||
false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
|
||||
createIsRestricted, canSwitchUsers);
|
||||
createIsRestricted(), canSwitchUsers,
|
||||
false /* isAddSupervisedUser */);
|
||||
checkIfAddUserDisallowedByAdminOnly(addUserRecord);
|
||||
records.add(addUserRecord);
|
||||
}
|
||||
|
||||
if (canCreateSupervisedUser()) {
|
||||
UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
|
||||
false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
|
||||
createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
|
||||
checkIfAddUserDisallowedByAdminOnly(addUserRecord);
|
||||
records.add(addUserRecord);
|
||||
}
|
||||
@@ -403,6 +402,40 @@ public class UserSwitcherController implements Dumpable {
|
||||
}.execute((SparseArray) bitmaps);
|
||||
}
|
||||
|
||||
boolean systemCanCreateUsers() {
|
||||
return !mUserManager.hasBaseUserRestriction(
|
||||
UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
|
||||
}
|
||||
|
||||
boolean currentUserCanCreateUsers() {
|
||||
UserInfo currentUser = mUserTracker.getUserInfo();
|
||||
return currentUser != null
|
||||
&& (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM)
|
||||
&& systemCanCreateUsers();
|
||||
}
|
||||
|
||||
boolean anyoneCanCreateUsers() {
|
||||
return systemCanCreateUsers() && mAddUsersFromLockScreen;
|
||||
}
|
||||
|
||||
boolean canCreateGuest(boolean hasExistingGuest) {
|
||||
return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
|
||||
&& !hasExistingGuest;
|
||||
}
|
||||
|
||||
boolean canCreateUser() {
|
||||
return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
|
||||
&& mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
|
||||
}
|
||||
|
||||
boolean createIsRestricted() {
|
||||
return !mAddUsersFromLockScreen;
|
||||
}
|
||||
|
||||
boolean canCreateSupervisedUser() {
|
||||
return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser();
|
||||
}
|
||||
|
||||
private void pauseRefreshUsers() {
|
||||
if (!mPauseRefreshUsers) {
|
||||
mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
|
||||
@@ -485,6 +518,9 @@ public class UserSwitcherController implements Dumpable {
|
||||
} else if (record.isAddUser) {
|
||||
showAddUserDialog(dialogShower);
|
||||
return;
|
||||
} else if (record.isAddSupervisedUser) {
|
||||
startSupervisedUserActivity();
|
||||
return;
|
||||
} else {
|
||||
id = record.info.id;
|
||||
}
|
||||
@@ -561,6 +597,22 @@ public class UserSwitcherController implements Dumpable {
|
||||
}
|
||||
}
|
||||
|
||||
private void startSupervisedUserActivity() {
|
||||
final Intent intent = new Intent()
|
||||
.setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
|
||||
.setPackage(mCreateSupervisedUserPackage)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
// TODO(b/209659998): [to-be-removed] fallback activity for supervised user creation.
|
||||
if (mContext.getPackageManager().resolveActivity(intent, 0) == null) {
|
||||
intent.setPackage(null)
|
||||
.setClassName("com.android.settings",
|
||||
"com.android.settings.users.AddSupervisedUserActivity");
|
||||
}
|
||||
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
private void listenForCallState() {
|
||||
mTelephonyListenerManager.addCallStateListener(mPhoneStateListener);
|
||||
}
|
||||
@@ -941,6 +993,8 @@ public class UserSwitcherController implements Dumpable {
|
||||
}
|
||||
} else if (item.isAddUser) {
|
||||
return context.getString(R.string.user_add_user);
|
||||
} else if (item.isAddSupervisedUser) {
|
||||
return context.getString(R.string.add_user_supervised);
|
||||
} else {
|
||||
return item.info.name;
|
||||
}
|
||||
@@ -955,9 +1009,11 @@ public class UserSwitcherController implements Dumpable {
|
||||
protected static Drawable getIconDrawable(Context context, UserRecord item) {
|
||||
int iconRes;
|
||||
if (item.isAddUser) {
|
||||
iconRes = R.drawable.ic_add_circle;
|
||||
iconRes = R.drawable.ic_account_circle;
|
||||
} else if (item.isGuest) {
|
||||
iconRes = R.drawable.ic_avatar_guest_user;
|
||||
iconRes = R.drawable.ic_account_circle_filled;
|
||||
} else if (item.isAddSupervisedUser) {
|
||||
iconRes = R.drawable.ic_add_supervised_user;
|
||||
} else {
|
||||
iconRes = R.drawable.ic_avatar_user;
|
||||
}
|
||||
@@ -1000,6 +1056,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
public final boolean isGuest;
|
||||
public final boolean isCurrent;
|
||||
public final boolean isAddUser;
|
||||
public final boolean isAddSupervisedUser;
|
||||
/** If true, the record is only visible to the owner and only when unlocked. */
|
||||
public final boolean isRestricted;
|
||||
public boolean isDisabledByAdmin;
|
||||
@@ -1007,7 +1064,8 @@ public class UserSwitcherController implements Dumpable {
|
||||
public boolean isSwitchToEnabled;
|
||||
|
||||
public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
|
||||
boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
|
||||
boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled,
|
||||
boolean isAddSupervisedUser) {
|
||||
this.info = info;
|
||||
this.picture = picture;
|
||||
this.isGuest = isGuest;
|
||||
@@ -1015,11 +1073,12 @@ public class UserSwitcherController implements Dumpable {
|
||||
this.isAddUser = isAddUser;
|
||||
this.isRestricted = isRestricted;
|
||||
this.isSwitchToEnabled = isSwitchToEnabled;
|
||||
this.isAddSupervisedUser = isAddSupervisedUser;
|
||||
}
|
||||
|
||||
public UserRecord copyWithIsCurrent(boolean _isCurrent) {
|
||||
return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
|
||||
isSwitchToEnabled);
|
||||
isSwitchToEnabled, isAddSupervisedUser);
|
||||
}
|
||||
|
||||
public int resolveId() {
|
||||
@@ -1043,6 +1102,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
}
|
||||
if (isGuest) sb.append(" <isGuest>");
|
||||
if (isAddUser) sb.append(" <isAddUser>");
|
||||
if (isAddSupervisedUser) sb.append(" <isAddSupervisedUser>");
|
||||
if (isCurrent) sb.append(" <isCurrent>");
|
||||
if (picture != null) sb.append(" <hasPicture>");
|
||||
if (isRestricted) sb.append(" <isRestricted>");
|
||||
|
||||
@@ -27,11 +27,10 @@ import android.graphics.drawable.InsetDrawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.UserManager
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowInsets.Type
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageView
|
||||
@@ -71,8 +70,18 @@ class UserSwitcherActivity @Inject constructor(
|
||||
private lateinit var broadcastReceiver: BroadcastReceiver
|
||||
private var popupMenu: UserSwitcherPopupMenu? = null
|
||||
private lateinit var addButton: View
|
||||
private var addUserItem: UserRecord? = null
|
||||
private var addGuestItem: UserRecord? = null
|
||||
private var addUserRecords = mutableListOf<UserRecord>()
|
||||
// When the add users options become available, insert another option to manage users
|
||||
private val manageUserRecord = UserRecord(
|
||||
null /* info */,
|
||||
null /* picture */,
|
||||
false /* isGuest */,
|
||||
false /* isCurrent */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
false /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */
|
||||
)
|
||||
|
||||
private val adapter = object : BaseUserAdapter(userSwitcherController) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
@@ -104,7 +113,18 @@ class UserSwitcherActivity @Inject constructor(
|
||||
return view
|
||||
}
|
||||
|
||||
override fun getName(context: Context, item: UserRecord): String {
|
||||
return if (item == manageUserRecord) {
|
||||
getString(R.string.manage_users)
|
||||
} else {
|
||||
super.getName(context, item)
|
||||
}
|
||||
}
|
||||
|
||||
fun findUserIcon(item: UserRecord): Drawable {
|
||||
if (item == manageUserRecord) {
|
||||
return getDrawable(R.drawable.ic_manage_users)
|
||||
}
|
||||
if (item.info == null) {
|
||||
return getIconDrawable(this@UserSwitcherActivity, item)
|
||||
}
|
||||
@@ -169,20 +189,11 @@ class UserSwitcherActivity @Inject constructor(
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.user_switcher_fullscreen)
|
||||
window.decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
|
||||
|
||||
parent = requireViewById<ViewGroup>(R.id.user_switcher_root).apply {
|
||||
setOnApplyWindowInsetsListener {
|
||||
v: View, insets: WindowInsets ->
|
||||
v.apply {
|
||||
val l = getPaddingLeft()
|
||||
val t = getPaddingTop()
|
||||
val r = getPaddingRight()
|
||||
setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
|
||||
}
|
||||
|
||||
WindowInsets.CONSUMED
|
||||
}
|
||||
}
|
||||
parent = requireViewById<ViewGroup>(R.id.user_switcher_root)
|
||||
|
||||
requireViewById<View>(R.id.cancel).apply {
|
||||
setOnClickListener {
|
||||
@@ -203,15 +214,19 @@ class UserSwitcherActivity @Inject constructor(
|
||||
|
||||
private fun showPopupMenu() {
|
||||
val items = mutableListOf<UserRecord>()
|
||||
addUserItem?.let { items.add(it) }
|
||||
addGuestItem?.let { items.add(it) }
|
||||
addUserRecords.forEach { items.add(it) }
|
||||
|
||||
var popupMenuAdapter = ItemAdapter(
|
||||
this,
|
||||
R.layout.user_switcher_fullscreen_popup_item,
|
||||
layoutInflater,
|
||||
{ item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item) },
|
||||
{ item: UserRecord -> adapter.findUserIcon(item) }
|
||||
{ item: UserRecord -> adapter.findUserIcon(item).mutate().apply {
|
||||
setTint(resources.getColor(
|
||||
R.color.user_switcher_fullscreen_popup_item_tint,
|
||||
getTheme()
|
||||
))
|
||||
} }
|
||||
)
|
||||
popupMenuAdapter.addAll(items)
|
||||
|
||||
@@ -225,10 +240,17 @@ class UserSwitcherActivity @Inject constructor(
|
||||
}
|
||||
// -1 for the header
|
||||
val item = popupMenuAdapter.getItem(pos - 1)
|
||||
adapter.onUserListItemClicked(item)
|
||||
if (item == manageUserRecord) {
|
||||
val i = Intent().setAction(Settings.ACTION_USER_SETTINGS)
|
||||
this@UserSwitcherActivity.startActivity(i)
|
||||
} else {
|
||||
adapter.onUserListItemClicked(item)
|
||||
}
|
||||
|
||||
dismiss()
|
||||
popupMenu = null
|
||||
|
||||
this@UserSwitcherActivity.finish()
|
||||
}
|
||||
|
||||
show()
|
||||
@@ -245,14 +267,15 @@ class UserSwitcherActivity @Inject constructor(
|
||||
}
|
||||
}
|
||||
parent.removeViews(start, count)
|
||||
addUserRecords.clear()
|
||||
|
||||
val flow = requireViewById<Flow>(R.id.flow)
|
||||
for (i in 0 until adapter.getCount()) {
|
||||
val item = adapter.getItem(i)
|
||||
if (item.isAddUser) {
|
||||
addUserItem = item
|
||||
} else if (item.isGuest && item.info == null) {
|
||||
addGuestItem = item
|
||||
if (item.isAddUser ||
|
||||
item.isAddSupervisedUser ||
|
||||
item.isGuest && item.info == null) {
|
||||
addUserRecords.add(item)
|
||||
} else {
|
||||
val userView = adapter.getView(i, null, parent)
|
||||
userView.setId(View.generateViewId())
|
||||
@@ -273,7 +296,8 @@ class UserSwitcherActivity @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (addUserItem != null || addGuestItem != null) {
|
||||
if (!addUserRecords.isEmpty()) {
|
||||
addUserRecords.add(manageUserRecord)
|
||||
addButton.visibility = View.VISIBLE
|
||||
} else {
|
||||
addButton.visibility = View.GONE
|
||||
|
||||
@@ -42,7 +42,7 @@ class UserSwitcherPopupMenu(
|
||||
setBackgroundDrawable(
|
||||
res.getDrawable(R.drawable.bouncer_user_switcher_popup_bg, context.getTheme())
|
||||
)
|
||||
setModal(true)
|
||||
setModal(false)
|
||||
setOverlapAnchor(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */,
|
||||
0 /* flags */);
|
||||
users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
|
||||
false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */));
|
||||
false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */));
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
@@ -147,5 +147,6 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
|
||||
current,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
}
|
||||
|
||||
@@ -193,5 +193,6 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
|
||||
isCurrentUser,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -130,6 +131,21 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
.thenReturn(true)
|
||||
`when`(notificationShadeWindowView.context).thenReturn(context)
|
||||
|
||||
// Since userSwitcherController involves InteractionJankMonitor.
|
||||
// Let's fulfill the dependencies.
|
||||
val mockedContext = mock(Context::class.java)
|
||||
doReturn(mockedContext).`when`(notificationShadeWindowView).context
|
||||
doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow
|
||||
doNothing().`when`(threadedRenderer).addObserver(any())
|
||||
doNothing().`when`(threadedRenderer).removeObserver(any())
|
||||
doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer
|
||||
|
||||
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
||||
|
||||
setupController()
|
||||
}
|
||||
|
||||
private fun setupController() {
|
||||
userSwitcherController = UserSwitcherController(
|
||||
context,
|
||||
activityManager,
|
||||
@@ -153,18 +169,6 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
dumpManager,
|
||||
dialogLaunchAnimator)
|
||||
userSwitcherController.mPauseRefreshUsers = true
|
||||
|
||||
// Since userSwitcherController involves InteractionJankMonitor.
|
||||
// Let's fulfill the dependencies.
|
||||
val mockedContext = mock(Context::class.java)
|
||||
doReturn(mockedContext).`when`(notificationShadeWindowView).context
|
||||
doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow
|
||||
doNothing().`when`(threadedRenderer).addObserver(any())
|
||||
doNothing().`when`(threadedRenderer).removeObserver(any())
|
||||
doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer
|
||||
userSwitcherController.init(notificationShadeWindowView)
|
||||
|
||||
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
||||
userSwitcherController.init(notificationShadeWindowView)
|
||||
}
|
||||
|
||||
@@ -177,7 +181,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
@@ -196,7 +201,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
@@ -220,7 +226,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
@@ -240,7 +247,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -262,7 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -283,7 +292,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -302,7 +312,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -323,7 +334,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -357,7 +369,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
true /* isSwitchToEnabled */,
|
||||
false /* isAddSupervisedUser */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
@@ -389,7 +402,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
userSwitcherController.users.add(UserSwitcherController.UserRecord(
|
||||
UserInfo(id, name, 0),
|
||||
null, false, isCurrent, false,
|
||||
false, false
|
||||
false, false, false
|
||||
))
|
||||
}
|
||||
val bgUserName = "background_user"
|
||||
@@ -412,4 +425,42 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
`when`(userTracker.userId).thenReturn(1)
|
||||
assertEquals(false, userSwitcherController.isSystemUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCanCreateSupervisedUserWithConfiguredPackage() {
|
||||
// GIVEN the supervised user creation package is configured
|
||||
`when`(context.getString(
|
||||
com.android.internal.R.string.config_supervisedUserCreationPackage))
|
||||
.thenReturn("some_pkg")
|
||||
|
||||
// AND the current user is allowed to create new users
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
// WHEN the controller is started with the above config
|
||||
setupController()
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
// THEN a supervised user can be constructed
|
||||
assertTrue(userSwitcherController.canCreateSupervisedUser())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCannotCreateSupervisedUserWithConfiguredPackage() {
|
||||
// GIVEN the supervised user creation package is NOT configured
|
||||
`when`(context.getString(
|
||||
com.android.internal.R.string.config_supervisedUserCreationPackage))
|
||||
.thenReturn(null)
|
||||
|
||||
// AND the current user is allowed to create new users
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
// WHEN the controller is started with the above config
|
||||
setupController()
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
// THEN a supervised user can NOT be constructed
|
||||
assertFalse(userSwitcherController.canCreateSupervisedUser())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user