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.systemui.statusbar.policy;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.content.pm.UserInfo;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.graphics.Bitmap;
28 import android.graphics.drawable.Drawable;
29 import android.os.AsyncTask;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.provider.ContactsContract;
33 import android.util.Log;
34 
35 import androidx.annotation.NonNull;
36 
37 import com.android.internal.util.UserIcons;
38 import com.android.settingslib.drawable.UserIconDrawable;
39 import com.android.systemui.R;
40 import com.android.systemui.dagger.SysUISingleton;
41 import com.android.systemui.dagger.qualifiers.Main;
42 import com.android.systemui.settings.UserTracker;
43 
44 import java.util.ArrayList;
45 import java.util.concurrent.Executor;
46 
47 import javax.inject.Inject;
48 
49 /**
50  */
51 @SysUISingleton
52 public class UserInfoControllerImpl implements UserInfoController {
53 
54     private static final String TAG = "UserInfoController";
55 
56     private final Context mContext;
57     private final UserTracker mUserTracker;
58     private final ArrayList<OnUserInfoChangedListener> mCallbacks =
59             new ArrayList<OnUserInfoChangedListener>();
60     private AsyncTask<Void, Void, UserInfoQueryResult> mUserInfoTask;
61 
62     private String mUserName;
63     private Drawable mUserDrawable;
64     private String mUserAccount;
65 
66     /**
67      */
68     @Inject
UserInfoControllerImpl(Context context, @Main Executor mainExecutor, UserTracker userTracker)69     public UserInfoControllerImpl(Context context, @Main Executor mainExecutor,
70             UserTracker userTracker) {
71         mContext = context;
72         mUserTracker = userTracker;
73         mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
74 
75         IntentFilter profileFilter = new IntentFilter();
76         profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
77         profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
78         mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
79                 null, null, Context.RECEIVER_EXPORTED_UNAUDITED);
80     }
81 
82     @Override
addCallback(@onNull OnUserInfoChangedListener callback)83     public void addCallback(@NonNull OnUserInfoChangedListener callback) {
84         mCallbacks.add(callback);
85         callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
86     }
87 
88     @Override
removeCallback(@onNull OnUserInfoChangedListener callback)89     public void removeCallback(@NonNull OnUserInfoChangedListener callback) {
90         mCallbacks.remove(callback);
91     }
92 
93     private final UserTracker.Callback mUserChangedCallback =
94             new UserTracker.Callback() {
95                 @Override
96                 public void onUserChanged(int newUser, @NonNull Context userContext) {
97                     reloadUserInfo();
98                 }
99             };
100 
101     private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
102         @Override
103         public void onReceive(Context context, Intent intent) {
104             final String action = intent.getAction();
105             if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
106                     Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
107                 final int currentUser = mUserTracker.getUserId();
108                 final int changedUser =
109                         intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
110                 if (changedUser == currentUser) {
111                     reloadUserInfo();
112                 }
113             }
114         }
115     };
116 
reloadUserInfo()117     public void reloadUserInfo() {
118         if (mUserInfoTask != null) {
119             mUserInfoTask.cancel(false);
120             mUserInfoTask = null;
121         }
122         queryForUserInformation();
123     }
124 
queryForUserInformation()125     private void queryForUserInformation() {
126         Context currentUserContext;
127         UserInfo userInfo;
128         try {
129             userInfo = mUserTracker.getUserInfo();
130             currentUserContext = mContext.createPackageContextAsUser("android", 0,
131                     new UserHandle(userInfo.id));
132         } catch (PackageManager.NameNotFoundException e) {
133             Log.e(TAG, "Couldn't create user context", e);
134             throw new RuntimeException(e);
135         }
136         final int userId = userInfo.id;
137         final boolean isGuest = userInfo.isGuest();
138         final String userName = userInfo.name;
139         final boolean lightIcon = mContext.getThemeResId() != R.style.Theme_SystemUI_LightWallpaper;
140 
141         final Resources res = mContext.getResources();
142         final int avatarSize = Math.max(
143                 res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
144                 res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
145 
146         final Context context = currentUserContext;
147         mUserInfoTask = new AsyncTask<Void, Void, UserInfoQueryResult>() {
148 
149             @Override
150             protected UserInfoQueryResult doInBackground(Void... params) {
151                 final UserManager um = UserManager.get(mContext);
152 
153                 // Fall back to the UserManager nickname if we can't read the name from the local
154                 // profile below.
155                 String name = userName;
156                 Drawable avatar = null;
157                 Bitmap rawAvatar = um.getUserIcon(userId);
158                 if (rawAvatar != null) {
159                     avatar = new UserIconDrawable(avatarSize)
160                             .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
161                 } else {
162                     avatar = UserIcons.getDefaultUserIcon(
163                             context.getResources(),
164                             isGuest? UserHandle.USER_NULL : userId,
165                             lightIcon);
166                 }
167 
168                 // If it's a single-user device, get the profile name, since the nickname is not
169                 // usually valid
170                 if (um.getUsers().size() <= 1) {
171                     // Try and read the display name from the local profile
172                     final Cursor cursor = context.getContentResolver().query(
173                             ContactsContract.Profile.CONTENT_URI, new String[] {
174                                     ContactsContract.CommonDataKinds.Phone._ID,
175                                     ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
176                             }, null, null, null);
177                     if (cursor != null) {
178                         try {
179                             if (cursor.moveToFirst()) {
180                                 name = cursor.getString(cursor.getColumnIndex(
181                                         ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
182                             }
183                         } finally {
184                             cursor.close();
185                         }
186                     }
187                 }
188                 String userAccount = um.getUserAccount(userId);
189                 return new UserInfoQueryResult(name, avatar, userAccount);
190             }
191 
192             @Override
193             protected void onPostExecute(UserInfoQueryResult result) {
194                 mUserName = result.getName();
195                 mUserDrawable = result.getAvatar();
196                 mUserAccount = result.getUserAccount();
197                 mUserInfoTask = null;
198                 notifyChanged();
199             }
200         };
201         mUserInfoTask.execute();
202     }
203 
notifyChanged()204     private void notifyChanged() {
205         for (OnUserInfoChangedListener listener : mCallbacks) {
206             listener.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
207         }
208     }
209 
onDensityOrFontScaleChanged()210     public void onDensityOrFontScaleChanged() {
211         reloadUserInfo();
212     }
213 
214     private static class UserInfoQueryResult {
215         private String mName;
216         private Drawable mAvatar;
217         private String mUserAccount;
218 
UserInfoQueryResult(String name, Drawable avatar, String userAccount)219         public UserInfoQueryResult(String name, Drawable avatar, String userAccount) {
220             mName = name;
221             mAvatar = avatar;
222             mUserAccount = userAccount;
223         }
224 
getName()225         public String getName() {
226             return mName;
227         }
228 
getAvatar()229         public Drawable getAvatar() {
230             return mAvatar;
231         }
232 
getUserAccount()233         public String getUserAccount() {
234             return mUserAccount;
235         }
236     }
237 }
238