1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.dashboard.profileselector; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER; 20 import static android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER; 21 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER; 22 23 import android.app.ActivityManager; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.admin.DevicePolicyResourcesManager; 26 import android.content.Context; 27 import android.content.pm.UserInfo; 28 import android.graphics.drawable.Drawable; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.Log; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.widget.BaseAdapter; 36 import android.widget.ImageView; 37 import android.widget.TextView; 38 39 import com.android.internal.util.UserIcons; 40 import com.android.internal.widget.RecyclerView; 41 import com.android.settings.R; 42 import com.android.settingslib.Utils; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Objects; 47 48 /** 49 * Adapter for a spinner that shows a list of users. 50 */ 51 public class UserAdapter extends BaseAdapter { 52 private static final String TAG = "UserAdapter"; 53 54 /** Holder for user details */ 55 public static class UserDetails { 56 private final UserHandle mUserHandle; 57 private final UserManager mUserManager; 58 private final Drawable mIcon; 59 private final String mTitle; 60 UserDetails(UserHandle userHandle, UserManager um, Context context)61 public UserDetails(UserHandle userHandle, UserManager um, Context context) { 62 mUserHandle = userHandle; 63 mUserManager = um; 64 UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier()); 65 int tintColor = Utils.getColorAttrDefaultColor(context, 66 com.android.internal.R.attr.materialColorPrimary); 67 if (userInfo.isManagedProfile() 68 || (android.os.Flags.allowPrivateProfile() 69 && android.multiuser.Flags.enablePrivateSpaceFeatures() 70 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() 71 && userInfo.isPrivateProfile())) { 72 mIcon = context.getPackageManager().getUserBadgeForDensityNoBackground( 73 userHandle, /* density= */ 0); 74 mIcon.setTint(tintColor); 75 } else { 76 mIcon = UserIcons.getDefaultUserIconInColor(context.getResources(), tintColor); 77 } 78 mTitle = getTitle(context); 79 } 80 getTitle(Context context)81 private String getTitle(Context context) { 82 DevicePolicyManager devicePolicyManager = 83 Objects.requireNonNull(context.getSystemService(DevicePolicyManager.class)); 84 DevicePolicyResourcesManager resources = devicePolicyManager.getResources(); 85 int userId = mUserHandle.getIdentifier(); 86 if (userId == UserHandle.USER_CURRENT || userId == ActivityManager.getCurrentUser()) { 87 return resources.getString(PERSONAL_CATEGORY_HEADER, 88 () -> context.getString( 89 com.android.settingslib.R.string.category_personal)); 90 } else if (mUserManager.isManagedProfile(userId)) { 91 return resources.getString(WORK_CATEGORY_HEADER, 92 () -> context.getString(com.android.settingslib.R.string.category_work)); 93 } else if (android.os.Flags.allowPrivateProfile() 94 && android.multiuser.Flags.enablePrivateSpaceFeatures() 95 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() 96 && mUserManager.getUserInfo(userId).isPrivateProfile()) { 97 return resources.getString(PRIVATE_CATEGORY_HEADER, 98 () -> context.getString(com.android.settingslib.R.string.category_private)); 99 } 100 Log.w(TAG, "title requested for unexpected user id " + userId); 101 return resources.getString(PERSONAL_CATEGORY_HEADER, 102 () -> context.getString(com.android.settingslib.R.string.category_personal)); 103 } 104 } 105 106 private final ArrayList<UserDetails> mUserDetails; 107 private final LayoutInflater mInflater; 108 UserAdapter(Context context, ArrayList<UserDetails> users)109 public UserAdapter(Context context, ArrayList<UserDetails> users) { 110 if (users == null) { 111 throw new IllegalArgumentException("A list of user details must be provided"); 112 } 113 mUserDetails = users; 114 mInflater = context.getSystemService(LayoutInflater.class); 115 } 116 getUserHandle(int position)117 public UserHandle getUserHandle(int position) { 118 if (position < 0 || position >= mUserDetails.size()) { 119 return null; 120 } 121 return mUserDetails.get(position).mUserHandle; 122 } 123 124 @Override getView(int position, View convertView, ViewGroup parent)125 public View getView(int position, View convertView, ViewGroup parent) { 126 ViewHolder holder; 127 if (convertView != null) { 128 holder = (ViewHolder) convertView.getTag(); 129 } else { 130 convertView = mInflater.inflate(R.layout.user_preference, parent, false); 131 holder = new ViewHolder(convertView); 132 convertView.setTag(holder); 133 } 134 bindViewHolder(holder, position); 135 return convertView; 136 } 137 bindViewHolder(ViewHolder holder, int position)138 private void bindViewHolder(ViewHolder holder, int position) { 139 UserDetails userDetails = getItem(position); 140 holder.getIconView().setImageDrawable(userDetails.mIcon); 141 holder.setTitle(userDetails.mTitle); 142 } 143 144 @Override getCount()145 public int getCount() { 146 return mUserDetails.size(); 147 } 148 149 @Override getItem(int position)150 public UserAdapter.UserDetails getItem(int position) { 151 return mUserDetails.get(position); 152 } 153 154 @Override getItemId(int position)155 public long getItemId(int position) { 156 return mUserDetails.get(position).mUserHandle.getIdentifier(); 157 } 158 createRecyclerViewAdapter( OnClickListener onClickListener)159 private RecyclerView.Adapter<ViewHolder> createRecyclerViewAdapter( 160 OnClickListener onClickListener) { 161 return new RecyclerView.Adapter<ViewHolder>() { 162 @Override 163 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 164 View view = LayoutInflater.from(parent.getContext()) 165 .inflate(R.layout.user_select_item, parent, false); 166 167 return new ViewHolder(view, onClickListener); 168 } 169 170 @Override 171 public void onBindViewHolder(ViewHolder holder, int position) { 172 UserAdapter.this.bindViewHolder(holder, position); 173 } 174 175 @Override 176 public int getItemCount() { 177 return getCount(); 178 } 179 }; 180 } 181 182 /** 183 * Creates a {@link UserAdapter} if there is more than one profile on the device. 184 * 185 * <p> The adapter can be used to populate a spinner that switches between the different 186 * profiles. 187 * 188 * @return a {@link UserAdapter} or null if there is only one profile. 189 */ 190 public static UserAdapter createUserSpinnerAdapter(UserManager userManager, Context context) { 191 List<UserHandle> userProfiles = userManager.getUserProfiles(); 192 if (userProfiles.size() < 2) { 193 return null; 194 } 195 196 UserHandle myUserHandle = new UserHandle(UserHandle.myUserId()); 197 // The first option should be the current profile 198 userProfiles.remove(myUserHandle); 199 userProfiles.add(0, myUserHandle); 200 201 return createUserAdapter(userManager, context, userProfiles); 202 } 203 204 /** 205 * Creates a {@link RecyclerView} adapter which be used to populate a {@link RecyclerView} that 206 * select one of the different profiles. 207 */ 208 public static RecyclerView.Adapter<ViewHolder> createUserRecycleViewAdapter( 209 Context context, List<UserHandle> userProfiles, OnClickListener onClickListener) { 210 UserManager systemService = context.getSystemService(UserManager.class); 211 return createUserAdapter(systemService, context, userProfiles) 212 .createRecyclerViewAdapter(onClickListener); 213 } 214 215 private static UserAdapter createUserAdapter( 216 UserManager userManager, Context context, List<UserHandle> userProfiles) { 217 updateUserHandlesIfNeeded(userManager, userProfiles); 218 ArrayList<UserDetails> userDetails = new ArrayList<>(userProfiles.size()); 219 for (UserHandle userProfile : userProfiles) { 220 userDetails.add(new UserDetails(userProfile, userManager, context)); 221 } 222 return new UserAdapter(context, userDetails); 223 } 224 225 private static void updateUserHandlesIfNeeded( 226 UserManager userManager, List<UserHandle> userProfiles) { 227 for (int i = userProfiles.size() - 1; i >= 0; --i) { 228 if (com.android.settings.Utils.shouldHideUser(userProfiles.get(i), userManager)) { 229 userProfiles.remove(i); 230 } 231 } 232 } 233 234 static class ViewHolder extends RecyclerView.ViewHolder { 235 private final ImageView mIconView; 236 private final TextView mTitleView; 237 private final View mButtonView; 238 239 private ViewHolder(View view) { 240 super(view); 241 mIconView = view.findViewById(android.R.id.icon); 242 mTitleView = view.findViewById(android.R.id.title); 243 mButtonView = view.findViewById(R.id.button); 244 } 245 246 private ViewHolder(View view, OnClickListener onClickListener) { 247 this(view); 248 if (mButtonView != null) { 249 mButtonView.setOnClickListener(v -> onClickListener.onClick(getAdapterPosition())); 250 } 251 } 252 253 private ImageView getIconView() { 254 return mIconView; 255 } 256 257 private void setTitle(CharSequence title) { 258 mTitleView.setText(title); 259 if (mButtonView != null) { 260 mButtonView.setContentDescription(title); 261 } 262 } 263 } 264 265 /** 266 * Interface definition for a callback to be invoked when a user is clicked. 267 */ 268 public interface OnClickListener { 269 /** 270 * Called when a user has been clicked. 271 */ 272 void onClick(int position); 273 } 274 } 275