UserSwitcherController.java revision 4d75c079f35d85b687d8349e5e2940447d01198e
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 17package com.android.systemui.statusbar.policy; 18 19import com.android.systemui.BitmapHelper; 20import com.android.systemui.R; 21import com.android.systemui.qs.QSTile; 22import com.android.systemui.qs.tiles.UserDetailView; 23 24import android.app.ActivityManager; 25import android.app.ActivityManagerNative; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.pm.UserInfo; 31import android.graphics.Bitmap; 32import android.os.AsyncTask; 33import android.os.RemoteException; 34import android.os.UserHandle; 35import android.os.UserManager; 36import android.util.Log; 37import android.util.SparseArray; 38import android.view.View; 39import android.view.ViewGroup; 40import android.view.WindowManagerGlobal; 41import android.widget.BaseAdapter; 42 43import java.io.FileDescriptor; 44import java.io.PrintWriter; 45import java.lang.ref.WeakReference; 46import java.util.ArrayList; 47import java.util.List; 48 49/** 50 * Keeps a list of all users on the device for user switching. 51 */ 52public class UserSwitcherController { 53 54 private static final String TAG = "UserSwitcherController"; 55 56 private final Context mContext; 57 private final UserManager mUserManager; 58 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); 59 60 private ArrayList<UserRecord> mUsers = new ArrayList<>(); 61 62 public UserSwitcherController(Context context) { 63 mContext = context; 64 mUserManager = UserManager.get(context); 65 IntentFilter filter = new IntentFilter(); 66 filter.addAction(Intent.ACTION_USER_ADDED); 67 filter.addAction(Intent.ACTION_USER_REMOVED); 68 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 69 filter.addAction(Intent.ACTION_USER_SWITCHED); 70 filter.addAction(Intent.ACTION_USER_STOPPING); 71 mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter, 72 null /* permission */, null /* scheduler */); 73 refreshUsers(UserHandle.USER_NULL); 74 } 75 76 /** 77 * Refreshes users from UserManager. 78 * 79 * The pictures are only loaded if they have not been loaded yet. 80 * 81 * @param forcePictureLoadForId forces the picture of the given user to be reloaded. 82 */ 83 private void refreshUsers(int forcePictureLoadForId) { 84 85 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size()); 86 final int N = mUsers.size(); 87 for (int i = 0; i < N; i++) { 88 UserRecord r = mUsers.get(i); 89 if (r == null || r.info == null 90 || r.info.id == forcePictureLoadForId || r.picture == null) { 91 continue; 92 } 93 bitmaps.put(r.info.id, r.picture); 94 } 95 96 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() { 97 @SuppressWarnings("unchecked") 98 @Override 99 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) { 100 final SparseArray<Bitmap> bitmaps = params[0]; 101 List<UserInfo> infos = mUserManager.getUsers(true); 102 if (infos == null) { 103 return null; 104 } 105 ArrayList<UserRecord> records = new ArrayList<>(infos.size()); 106 int currentId = ActivityManager.getCurrentUser(); 107 UserRecord guestRecord = null; 108 int avatarSize = mContext.getResources() 109 .getDimensionPixelSize(R.dimen.max_avatar_size); 110 111 for (UserInfo info : infos) { 112 boolean isCurrent = currentId == info.id; 113 if (info.isGuest()) { 114 guestRecord = new UserRecord(info, null /* picture */, 115 true /* isGuest */, isCurrent); 116 } else if (!info.isManagedProfile()) { 117 Bitmap picture = bitmaps.get(info.id); 118 if (picture == null) { 119 picture = mUserManager.getUserIcon(info.id); 120 } 121 if (picture != null) { 122 picture = BitmapHelper.createCircularClip( 123 picture, avatarSize, avatarSize); 124 } 125 records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent)); 126 } 127 } 128 129 if (guestRecord == null) { 130 records.add(new UserRecord(null /* info */, null /* picture */, 131 true /* isGuest */, false /* isCurrent */)); 132 } else { 133 records.add(guestRecord); 134 } 135 136 return records; 137 } 138 139 @Override 140 protected void onPostExecute(ArrayList<UserRecord> userRecords) { 141 if (userRecords != null) { 142 mUsers = userRecords; 143 notifyAdapters(); 144 } 145 } 146 }.execute((SparseArray)bitmaps); 147 } 148 149 private void notifyAdapters() { 150 for (int i = mAdapters.size() - 1; i >= 0; i--) { 151 BaseUserAdapter adapter = mAdapters.get(i).get(); 152 if (adapter != null) { 153 adapter.notifyDataSetChanged(); 154 } else { 155 mAdapters.remove(i); 156 } 157 } 158 } 159 160 public void switchTo(UserRecord record) { 161 int id; 162 if (record.isGuest && record.info == null) { 163 // No guest user. Create one. 164 id = mUserManager.createGuest(mContext, 165 mContext.getResources().getString(R.string.guest_nickname)).id; 166 } else { 167 id = record.info.id; 168 } 169 170 if (ActivityManager.getCurrentUser() == id) { 171 if (record.isGuest) { 172 exitGuest(id); 173 } 174 return; 175 } 176 177 switchToUserId(id); 178 } 179 180 private void switchToUserId(int id) { 181 try { 182 WindowManagerGlobal.getWindowManagerService().lockNow(null); 183 ActivityManagerNative.getDefault().switchUser(id); 184 } catch (RemoteException e) { 185 Log.e(TAG, "Couldn't switch user.", e); 186 } 187 } 188 189 private void exitGuest(int id) { 190 // TODO: show confirmation dialog 191 switchToUserId(UserHandle.USER_OWNER); 192 mUserManager.removeUser(id); 193 } 194 195 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 196 @Override 197 public void onReceive(Context context, Intent intent) { 198 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 199 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 200 final int N = mUsers.size(); 201 for (int i = 0; i < N; i++) { 202 UserRecord record = mUsers.get(i); 203 if (record.info == null) continue; 204 boolean shouldBeCurrent = record.info.id == currentId; 205 if (record.isCurrent != shouldBeCurrent) { 206 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); 207 } 208 } 209 notifyAdapters(); 210 } 211 int forcePictureLoadForId = UserHandle.USER_NULL; 212 if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { 213 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 214 UserHandle.USER_NULL); 215 } 216 refreshUsers(forcePictureLoadForId); 217 } 218 }; 219 220 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 221 pw.println("UserSwitcherController state:"); 222 pw.print(" mUsers.size="); pw.println(mUsers.size()); 223 for (int i = 0; i < mUsers.size(); i++) { 224 final UserRecord u = mUsers.get(i); 225 pw.print(" "); pw.println(u.toString()); 226 } 227 } 228 229 public static abstract class BaseUserAdapter extends BaseAdapter { 230 231 final UserSwitcherController mController; 232 233 protected BaseUserAdapter(UserSwitcherController controller) { 234 mController = controller; 235 controller.mAdapters.add(new WeakReference<>(this)); 236 } 237 238 @Override 239 public int getCount() { 240 return mController.mUsers.size(); 241 } 242 243 @Override 244 public UserRecord getItem(int position) { 245 return mController.mUsers.get(position); 246 } 247 248 @Override 249 public long getItemId(int position) { 250 return position; 251 } 252 253 public void switchTo(UserRecord record) { 254 mController.switchTo(record); 255 } 256 257 public String getName(Context context, UserRecord item) { 258 if (item.isGuest) { 259 if (item.isCurrent) { 260 return context.getString(R.string.guest_exit_guest); 261 } else { 262 return context.getString( 263 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); 264 } 265 } else { 266 return item.info.name; 267 } 268 } 269 } 270 271 public static final class UserRecord { 272 public final UserInfo info; 273 public final Bitmap picture; 274 public final boolean isGuest; 275 public final boolean isCurrent; 276 277 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent) { 278 this.info = info; 279 this.picture = picture; 280 this.isGuest = isGuest; 281 this.isCurrent = isCurrent; 282 } 283 284 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 285 return new UserRecord(info, picture, isGuest, _isCurrent); 286 } 287 288 public String toString() { 289 StringBuilder sb = new StringBuilder(); 290 sb.append("UserRecord("); 291 if (info != null) { 292 sb.append("name=\"" + info.name + "\" id=" + info.id); 293 } else { 294 sb.append("<add guest placeholder>"); 295 } 296 if (isGuest) { 297 sb.append(" <isGuest>"); 298 } 299 if (isCurrent) { 300 sb.append(" <isCurrent>"); 301 } 302 if (picture != null) { 303 sb.append(" <hasPicture>"); 304 } 305 sb.append(')'); 306 return sb.toString(); 307 } 308 } 309 310 public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() { 311 private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS"); 312 313 @Override 314 public int getTitle() { 315 return R.string.quick_settings_user_title; 316 } 317 318 @Override 319 public View createDetailView(Context context, View convertView, ViewGroup parent) { 320 if (!(convertView instanceof UserDetailView)) { 321 convertView = UserDetailView.inflate(context, parent, false); 322 } 323 UserDetailView v = (UserDetailView) convertView; 324 if (v.getAdapter() == null) { 325 v.createAndSetAdapter(UserSwitcherController.this); 326 } 327 return v; 328 } 329 330 @Override 331 public Intent getSettingsIntent() { 332 return USER_SETTINGS_INTENT; 333 } 334 335 @Override 336 public Boolean getToggleState() { 337 return null; 338 } 339 340 @Override 341 public void setToggleState(boolean state) { 342 } 343 }; 344} 345