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 android.app.ActivityManagerNative;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.pm.PackageManager;
25import android.content.pm.UserInfo;
26import android.content.res.Resources;
27import android.database.Cursor;
28import android.graphics.Bitmap;
29import android.graphics.drawable.Drawable;
30import android.os.AsyncTask;
31import android.os.RemoteException;
32import android.os.UserHandle;
33import android.os.UserManager;
34import android.provider.ContactsContract;
35import android.util.Log;
36import android.util.Pair;
37
38import com.android.internal.util.UserIcons;
39import com.android.settingslib.drawable.UserIconDrawable;
40import com.android.systemui.R;
41
42import java.util.ArrayList;
43
44public final class UserInfoController {
45
46    private static final String TAG = "UserInfoController";
47
48    private final Context mContext;
49    private final ArrayList<OnUserInfoChangedListener> mCallbacks =
50            new ArrayList<OnUserInfoChangedListener>();
51    private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
52
53    private String mUserName;
54    private Drawable mUserDrawable;
55
56    public UserInfoController(Context context) {
57        mContext = context;
58        IntentFilter filter = new IntentFilter();
59        filter.addAction(Intent.ACTION_USER_SWITCHED);
60        mContext.registerReceiver(mReceiver, filter);
61
62        IntentFilter profileFilter = new IntentFilter();
63        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
64        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
65        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
66                null, null);
67    }
68
69    public void addListener(OnUserInfoChangedListener callback) {
70        mCallbacks.add(callback);
71        callback.onUserInfoChanged(mUserName, mUserDrawable);
72    }
73
74    public void remListener(OnUserInfoChangedListener callback) {
75        mCallbacks.remove(callback);
76    }
77
78    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
79        @Override
80        public void onReceive(Context context, Intent intent) {
81            final String action = intent.getAction();
82            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
83                reloadUserInfo();
84            }
85        }
86    };
87
88    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
89        @Override
90        public void onReceive(Context context, Intent intent) {
91            final String action = intent.getAction();
92            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
93                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
94                try {
95                    final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
96                    final int changedUser =
97                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
98                    if (changedUser == currentUser) {
99                        reloadUserInfo();
100                    }
101                } catch (RemoteException e) {
102                    Log.e(TAG, "Couldn't get current user id for profile change", e);
103                }
104            }
105        }
106    };
107
108    public void reloadUserInfo() {
109        if (mUserInfoTask != null) {
110            mUserInfoTask.cancel(false);
111            mUserInfoTask = null;
112        }
113        queryForUserInformation();
114    }
115
116    private void queryForUserInformation() {
117        Context currentUserContext;
118        UserInfo userInfo;
119        try {
120            userInfo = ActivityManagerNative.getDefault().getCurrentUser();
121            currentUserContext = mContext.createPackageContextAsUser("android", 0,
122                    new UserHandle(userInfo.id));
123        } catch (PackageManager.NameNotFoundException e) {
124            Log.e(TAG, "Couldn't create user context", e);
125            throw new RuntimeException(e);
126        } catch (RemoteException e) {
127            Log.e(TAG, "Couldn't get user info", e);
128            throw new RuntimeException(e);
129        }
130        final int userId = userInfo.id;
131        final boolean isGuest = userInfo.isGuest();
132        final String userName = userInfo.name;
133
134        final Resources res = mContext.getResources();
135        final int avatarSize = Math.max(
136                res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
137                res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
138
139        final Context context = currentUserContext;
140        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
141            @Override
142            protected Pair<String, Drawable> doInBackground(Void... params) {
143                final UserManager um = UserManager.get(mContext);
144
145                // Fall back to the UserManager nickname if we can't read the name from the local
146                // profile below.
147                String name = userName;
148                Drawable avatar = null;
149                Bitmap rawAvatar = um.getUserIcon(userId);
150                if (rawAvatar != null) {
151                    avatar = new UserIconDrawable(avatarSize)
152                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
153                } else {
154                    avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
155                            /* light= */ true);
156                }
157
158                // If it's a single-user device, get the profile name, since the nickname is not
159                // usually valid
160                if (um.getUsers().size() <= 1) {
161                    // Try and read the display name from the local profile
162                    final Cursor cursor = context.getContentResolver().query(
163                            ContactsContract.Profile.CONTENT_URI, new String[] {
164                                    ContactsContract.CommonDataKinds.Phone._ID,
165                                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
166                            }, null, null, null);
167                    if (cursor != null) {
168                        try {
169                            if (cursor.moveToFirst()) {
170                                name = cursor.getString(cursor.getColumnIndex(
171                                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
172                            }
173                        } finally {
174                            cursor.close();
175                        }
176                    }
177                }
178                return new Pair<String, Drawable>(name, avatar);
179            }
180
181            @Override
182            protected void onPostExecute(Pair<String, Drawable> result) {
183                mUserName = result.first;
184                mUserDrawable = result.second;
185                mUserInfoTask = null;
186                notifyChanged();
187            }
188        };
189        mUserInfoTask.execute();
190    }
191
192    private void notifyChanged() {
193        for (OnUserInfoChangedListener listener : mCallbacks) {
194            listener.onUserInfoChanged(mUserName, mUserDrawable);
195        }
196    }
197
198    public void onDensityOrFontScaleChanged() {
199        reloadUserInfo();
200    }
201
202    public interface OnUserInfoChangedListener {
203        public void onUserInfoChanged(String name, Drawable picture);
204    }
205}
206