UserSwitcherController.java revision 00a0b1f397557790cf9ab55fe06e72a96ebc5353
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.R;
20import com.android.systemui.qs.QSTile;
21import com.android.systemui.qs.tiles.UserDetailView;
22
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.UserInfo;
30import android.graphics.Bitmap;
31import android.os.AsyncTask;
32import android.os.RemoteException;
33import android.os.UserManager;
34import android.util.Log;
35import android.view.View;
36import android.view.ViewGroup;
37import android.view.WindowManagerGlobal;
38import android.widget.BaseAdapter;
39
40import java.io.FileDescriptor;
41import java.io.PrintWriter;
42import java.lang.ref.WeakReference;
43import java.util.ArrayList;
44import java.util.List;
45
46/**
47 * Keeps a list of all users on the device for user switching.
48 */
49public class UserSwitcherController {
50
51    private static final String TAG = "UserSwitcherController";
52
53    private final Context mContext;
54    private final UserManager mUserManager;
55    private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
56
57    private ArrayList<UserRecord> mUsers = new ArrayList<>();
58
59    public UserSwitcherController(Context context) {
60        mContext = context;
61        mUserManager = UserManager.get(context);
62        IntentFilter filter = new IntentFilter();
63        filter.addAction(Intent.ACTION_USER_ADDED);
64        filter.addAction(Intent.ACTION_USER_REMOVED);
65        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
66        filter.addAction(Intent.ACTION_USER_SWITCHED);
67        mContext.registerReceiver(mReceiver, filter);
68        refreshUsers();
69    }
70
71    private void refreshUsers() {
72        new AsyncTask<Void, Void, ArrayList<UserRecord>>() {
73
74            @Override
75            protected ArrayList<UserRecord> doInBackground(Void... params) {
76                List<UserInfo> infos = mUserManager.getUsers(true);
77                if (infos == null) {
78                    return null;
79                }
80                ArrayList<UserRecord> records = new ArrayList<>(infos.size());
81                int currentId = ActivityManager.getCurrentUser();
82                UserRecord guestRecord = null;
83
84                for (UserInfo info : infos) {
85                    boolean isCurrent = currentId == info.id;
86                    if (info.isGuest()) {
87                        guestRecord = new UserRecord(info, null /* picture */,
88                                true /* isGuest */, isCurrent);
89                    } else if (!info.isManagedProfile()) {
90                        records.add(new UserRecord(info, mUserManager.getUserIcon(info.id),
91                                false /* isGuest */, isCurrent));
92                    }
93                }
94
95                if (guestRecord == null) {
96                    records.add(new UserRecord(null /* info */, null /* picture */,
97                            true /* isGuest */, false /* isCurrent */));
98                } else {
99                    records.add(guestRecord);
100                }
101
102                return records;
103            }
104
105            @Override
106            protected void onPostExecute(ArrayList<UserRecord> userRecords) {
107                if (userRecords != null) {
108                    mUsers = userRecords;
109                    notifyAdapters();
110                }
111            }
112        }.execute((Void[])null);
113    }
114
115    private void notifyAdapters() {
116        for (int i = mAdapters.size() - 1; i >= 0; i--) {
117            BaseUserAdapter adapter = mAdapters.get(i).get();
118            if (adapter != null) {
119                adapter.notifyDataSetChanged();
120            } else {
121                mAdapters.remove(i);
122            }
123        }
124    }
125
126    public void switchTo(UserRecord record) {
127        int id;
128        if (record.isGuest && record.info == null) {
129            // No guest user. Create one.
130            id = mUserManager.createGuest(mContext,
131                    mContext.getResources().getString(R.string.guest_nickname)).id;
132        } else {
133            id = record.info.id;
134        }
135
136        if (ActivityManager.getCurrentUser() == id) {
137            return;
138        }
139
140        try {
141            WindowManagerGlobal.getWindowManagerService().lockNow(null);
142            ActivityManagerNative.getDefault().switchUser(id);
143        } catch (RemoteException e) {
144            Log.e(TAG, "Couldn't switch user.", e);
145        }
146    }
147
148    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
149        @Override
150        public void onReceive(Context context, Intent intent) {
151            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
152                final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
153                final int N = mUsers.size();
154                for (int i = 0; i < N; i++) {
155                    UserRecord record = mUsers.get(i);
156                    boolean shouldBeCurrent = record.info.id == currentId;
157                    if (record.isCurrent != shouldBeCurrent) {
158                        mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
159                    }
160                }
161                notifyAdapters();
162            } else {
163                refreshUsers();
164            }
165        }
166    };
167
168    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
169        pw.println("UserSwitcherController state:");
170        pw.print("  mUsers.size="); pw.println(mUsers.size());
171        for (int i = 0; i < mUsers.size(); i++) {
172            final UserRecord u = mUsers.get(i);
173            pw.print("    "); pw.println(u.toString());
174        }
175    }
176
177    public static abstract class BaseUserAdapter extends BaseAdapter {
178
179        final UserSwitcherController mController;
180
181        protected BaseUserAdapter(UserSwitcherController controller) {
182            mController = controller;
183            controller.mAdapters.add(new WeakReference<>(this));
184        }
185
186        @Override
187        public int getCount() {
188            return mController.mUsers.size();
189        }
190
191        @Override
192        public UserRecord getItem(int position) {
193            return mController.mUsers.get(position);
194        }
195
196        @Override
197        public long getItemId(int position) {
198            return mController.mUsers.get(position).info.id;
199        }
200
201        public void switchTo(UserRecord record) {
202            mController.switchTo(record);
203        }
204    }
205
206    public static final class UserRecord {
207        public final UserInfo info;
208        public final Bitmap picture;
209        public final boolean isGuest;
210        public final boolean isCurrent;
211
212        public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent) {
213            this.info = info;
214            this.picture = picture;
215            this.isGuest = isGuest;
216            this.isCurrent = isCurrent;
217        }
218
219        public UserRecord copyWithIsCurrent(boolean _isCurrent) {
220            return new UserRecord(info, picture, isGuest, _isCurrent);
221        }
222
223        public String toString() {
224            StringBuilder sb = new StringBuilder();
225            sb.append("UserRecord(");
226            if (info != null) {
227                sb.append("name=\"" + info.name + "\" id=" + info.id);
228            } else {
229                sb.append("<add guest placeholder>");
230            }
231            if (isGuest) {
232                sb.append(" <isGuest>");
233            }
234            if (isCurrent) {
235                sb.append(" <isCurrent>");
236            }
237            if (picture != null) {
238                sb.append(" <hasPicture>");
239            }
240            sb.append(')');
241            return sb.toString();
242        }
243    }
244
245    public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() {
246        private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
247
248        @Override
249        public int getTitle() {
250            return R.string.quick_settings_user_title;
251        }
252
253        @Override
254        public View createDetailView(Context context, View convertView, ViewGroup parent) {
255            if (!(convertView instanceof UserDetailView)) {
256                convertView = UserDetailView.inflate(context, parent, false);
257            }
258            UserDetailView v = (UserDetailView) convertView;
259            if (v.getAdapter() == null) {
260                v.createAndSetAdapter(UserSwitcherController.this);
261            }
262            return v;
263        }
264
265        @Override
266        public Intent getSettingsIntent() {
267            return USER_SETTINGS_INTENT;
268        }
269
270        @Override
271        public Boolean getToggleState() {
272            return null;
273        }
274
275        @Override
276        public void setToggleState(boolean state) {
277        }
278    };
279}
280