User management UI: take 2

Single screen user management, also visible on secondary users.
Work in progress, especially with respect to synchronizing with
the Me profile.

Change-Id: Id6e94a85d53356847e4e019c52e3388de9ecb354
This commit is contained in:
Amith Yamasani
2012-09-06 18:04:06 -07:00
parent 1352c142a3
commit dc6bfa6ef5
15 changed files with 522 additions and 383 deletions

View File

@@ -16,52 +16,170 @@
package com.android.settings.users;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserSettings extends SettingsPreferenceFragment
implements OnPreferenceClickListener {
implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener,
Preference.OnPreferenceChangeListener {
private static final String TAG = "UserSettings";
private static final String KEY_USER_NICKNAME = "user_nickname";
private static final String KEY_USER_LIST = "user_list";
private static final String KEY_USER_ME = "user_me";
private static final int MENU_ADD_USER = Menu.FIRST;
private static final int MENU_REMOVE_USER = Menu.FIRST+1;
private static final int DIALOG_CONFIRM_REMOVE = 1;
private static final int DIALOG_ADD_USER = 2;
private static final int MESSAGE_UPDATE_LIST = 1;
private static final int[] USER_DRAWABLES = {
R.drawable.ic_user,
R.drawable.ic_user_cyan,
R.drawable.ic_user_green,
R.drawable.ic_user_purple,
R.drawable.ic_user_red,
R.drawable.ic_user_yellow
};
private static final String[] CONTACT_PROJECTION = new String[] {
Phone._ID, // 0
Phone.DISPLAY_NAME, // 1
};
private PreferenceGroup mUserListCategory;
private Preference mMePreference;
private EditTextPreference mNicknamePreference;
private int mRemovingUserId = -1;
private boolean mAddingUser;
private final Object mUserLock = new Object();
private UserManager mUserManager;
private boolean mProfileChanged;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_UPDATE_LIST:
updateUserList();
break;
}
}
};
private ContentObserver mProfileObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
mProfileChanged = true;
}
};
private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
addPreferencesFromResource(R.xml.user_settings);
mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
mMePreference = (Preference) findPreference(KEY_USER_ME);
mMePreference.setOnPreferenceClickListener(this);
if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
mMePreference.setSummary(null);
}
mNicknamePreference = (EditTextPreference) findPreference(KEY_USER_NICKNAME);
mNicknamePreference.setOnPreferenceChangeListener(this);
mNicknamePreference.setSummary(mUserManager.getUserInfo(UserHandle.myUserId()).name);
loadProfile(false);
setHasOptionsMenu(true);
// Register to watch for profile changes
getActivity().getContentResolver().registerContentObserver(
ContactsContract.Profile.CONTENT_URI, false, mProfileObserver);
getActivity().registerReceiver(mUserChangeReceiver,
new IntentFilter(Intent.ACTION_USER_REMOVED));
}
@Override
public void onResume() {
super.onResume();
if (mProfileChanged) {
loadProfile(true);
mProfileChanged = false;
}
updateUserList();
}
@Override
public void onDestroy() {
super.onDestroy();
getActivity().getContentResolver().unregisterContentObserver(mProfileObserver);
getActivity().unregisterReceiver(mUserChangeReceiver);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MenuItem addAccountItem = menu.add(0, MENU_ADD_USER, 0, R.string.user_add_user_menu);
addAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_WITH_TEXT);
if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
MenuItem addUserItem = menu.add(0, MENU_ADD_USER, 0, R.string.user_add_user_menu);
addUserItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_WITH_TEXT);
} else {
MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, 0, R.string.user_remove_user_menu);
removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
}
@Override
@@ -70,52 +188,270 @@ public class UserSettings extends SettingsPreferenceFragment
if (itemId == MENU_ADD_USER) {
onAddUserClicked();
return true;
} else if (itemId == MENU_REMOVE_USER) {
onRemoveUserClicked(UserHandle.myUserId());
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
private void loadProfile(boolean force) {
UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
if (force || user.iconPath == null || user.iconPath.equals("")) {
assignProfilePhoto(user);
}
String profileName = getProfileName();
mMePreference.setTitle(profileName);
}
private void onAddUserClicked() {
((PreferenceActivity) getActivity()).startPreferencePanel(
UserDetailsSettings.class.getName(), null, R.string.user_details_title,
null, this, 0);
synchronized (mUserLock) {
if (mRemovingUserId == -1 && !mAddingUser) {
showDialog(DIALOG_ADD_USER);
setOnDismissListener(this);
}
}
}
private void onRemoveUserClicked(int userId) {
synchronized (mUserLock) {
if (mRemovingUserId == -1 && !mAddingUser) {
mRemovingUserId = userId;
showDialog(DIALOG_CONFIRM_REMOVE);
setOnDismissListener(this);
}
}
}
@Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DIALOG_CONFIRM_REMOVE:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.user_confirm_remove_title)
.setMessage(R.string.user_confirm_remove_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
removeUserNow();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
case DIALOG_ADD_USER:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.user_add_user_title)
.setMessage(R.string.user_add_user_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
addUserNow();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
default:
return null;
}
}
private void removeUserNow() {
if (mRemovingUserId == UserHandle.myUserId()) {
removeThisUser();
} else {
new Thread() {
public void run() {
synchronized (mUserLock) {
// TODO: Show some progress while removing the user
mUserManager.removeUser(mRemovingUserId);
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
mRemovingUserId = -1;
}
}
}.start();
}
}
private void removeThisUser() {
// TODO:
Toast.makeText(getActivity(), "Not implemented yet!", Toast.LENGTH_SHORT).show();
synchronized (mUserLock) {
mRemovingUserId = -1;
}
}
private void addUserNow() {
synchronized (mUserLock) {
mAddingUser = true;
updateUserList();
new Thread() {
public void run() {
// Could take a few seconds
UserInfo user = mUserManager.createUser(
getActivity().getResources().getString(R.string.user_new_user_name), 0);
if (user != null) {
assignDefaultPhoto(user);
}
synchronized (mUserLock) {
mAddingUser = false;
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
}
}
}.start();
}
}
private void updateUserList() {
List<UserInfo> users = ((UserManager) getActivity().getSystemService(Context.USER_SERVICE))
.getUsers();
List<UserInfo> users = mUserManager.getUsers();
mUserListCategory.removeAll();
mUserListCategory.setOrderingAsAdded(false);
for (UserInfo user : users) {
Preference pref = new Preference(getActivity());
pref.setTitle(user.name);
pref.setOnPreferenceClickListener(this);
pref.setKey("id=" + user.id);
if (user.iconPath != null) {
setPhotoId(pref, user.iconPath);
Preference pref;
if (user.id == UserHandle.myUserId()) {
pref = mMePreference;
} else {
pref = new UserPreference(getActivity(), null, user.id,
UserHandle.myUserId() == UserHandle.USER_OWNER, this);
pref.setOnPreferenceClickListener(this);
pref.setKey("id=" + user.id);
mUserListCategory.addPreference(pref);
if (user.id == UserHandle.USER_OWNER) {
pref.setSummary(R.string.user_owner);
}
pref.setTitle(user.name);
}
if (user.iconPath != null) {
setPhotoId(pref, user);
}
}
// Add a temporary entry for the user being created
if (mAddingUser) {
Preference pref = new UserPreference(getActivity(), null, UserPreference.USERID_UNKNOWN,
false, null);
pref.setEnabled(false);
pref.setTitle(R.string.user_new_user_name);
pref.setSummary(R.string.user_adding_new_user);
pref.setIcon(R.drawable.ic_user);
mUserListCategory.addPreference(pref);
}
}
private void setPhotoId(Preference pref, String realPath) {
Drawable d = Drawable.createFromPath(realPath);
/* TODO: Put this in an AsyncTask */
private void assignProfilePhoto(final UserInfo user) {
// If the contact is "me", then use my local profile photo. Otherwise, build a
// uri to get the avatar of the contact.
Uri contactUri = Profile.CONTENT_URI;
InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
getActivity().getContentResolver(),
contactUri, true);
// If there's no profile photo, assign a default avatar
if (avatarDataStream == null) {
assignDefaultPhoto(user);
setPhotoId(mMePreference, user);
return;
}
ParcelFileDescriptor fd = mUserManager.setUserIcon(user.id);
FileOutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
byte[] buffer = new byte[4096];
int readSize;
try {
while ((readSize = avatarDataStream.read(buffer)) > 0) {
os.write(buffer, 0, readSize);
}
os.close();
avatarDataStream.close();
} catch (IOException ioe) {
Log.e(TAG, "Error copying profile photo " + ioe);
}
setPhotoId(mMePreference, user);
}
private String getProfileName() {
Cursor cursor = getActivity().getContentResolver().query(
Profile.CONTENT_URI, CONTACT_PROJECTION, null, null, null);
if (cursor == null) {
Log.w(TAG, "getProfileName() returned NULL cursor!"
+ " contact uri used " + Profile.CONTENT_URI);
return null;
}
try {
if (cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
}
} finally {
cursor.close();
}
return null;
}
private void assignDefaultPhoto(UserInfo user) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
USER_DRAWABLES[user.id % USER_DRAWABLES.length]);
ParcelFileDescriptor fd = mUserManager.setUserIcon(user.id);
if (fd != null) {
bitmap.compress(CompressFormat.PNG, 100,
new ParcelFileDescriptor.AutoCloseOutputStream(fd));
}
}
private void setPhotoId(Preference pref, UserInfo user) {
ParcelFileDescriptor fd = mUserManager.setUserIcon(user.id);
Drawable d = Drawable.createFromStream(new ParcelFileDescriptor.AutoCloseInputStream(fd),
user.iconPath);
if (d == null) return;
pref.setIcon(d);
}
private void setUserName(String name) {
mUserManager.setUserName(UserHandle.myUserId(), name);
mNicknamePreference.setSummary(name);
}
@Override
public boolean onPreferenceClick(Preference pref) {
String sid = pref.getKey();
if (sid != null && sid.startsWith("id=")) {
int id = Integer.parseInt(sid.substring(3));
Bundle args = new Bundle();
args.putInt(UserDetailsSettings.EXTRA_USER_ID, id);
((PreferenceActivity) getActivity()).startPreferencePanel(
UserDetailsSettings.class.getName(),
args, 0, pref.getTitle(), this, 0);
if (pref == mMePreference) {
Intent editProfile = new Intent(Intent.ACTION_EDIT);
editProfile.setData(ContactsContract.Profile.CONTENT_URI);
startActivity(editProfile);
}
return false;
}
@Override
public void onClick(View v) {
if (v.getTag() instanceof UserPreference) {
int userId = ((UserPreference) v.getTag()).getUserId();
onRemoveUserClicked(userId);
}
}
@Override
public void onDismiss(DialogInterface dialog) {
synchronized (mUserLock) {
mAddingUser = false;
mRemovingUserId = -1;
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mNicknamePreference) {
String value = (String) newValue;
if (preference == mNicknamePreference && value != null
&& value.length() > 0) {
setUserName(value);
}
return true;
}
return false;
}
}