diff options
author | Amith Yamasani <yamasani@google.com> | 2014-07-24 14:13:36 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2014-07-24 23:13:34 +0000 |
commit | ee3987475e1d29e8355bdb9e068149eb01e3fd43 (patch) | |
tree | d033d075b0796fca9bfe4491a54dd51b0eb4c95c /src/com/android/settings/users | |
parent | b8684d573e1357ee573f26d511a630b39e368d0f (diff) | |
download | packages_apps_Settings-ee3987475e1d29e8355bdb9e068149eb01e3fd43.tar.gz packages_apps_Settings-ee3987475e1d29e8355bdb9e068149eb01e3fd43.tar.bz2 packages_apps_Settings-ee3987475e1d29e8355bdb9e068149eb01e3fd43.zip |
Refactor User Profile editor dialog for reuse
Move the dialog code into a separate class.
Use it from RestrictedProfileSettings
and UserSettings for the current user.
Bug: 15761405
Change-Id: I0b9a95ba8463304525e6a4b8cf3b4ca77b15796f
Diffstat (limited to 'src/com/android/settings/users')
6 files changed, 589 insertions, 433 deletions
diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 56368ef46..9df8217b7 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -345,7 +345,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen return getPreferenceScreen(); } - protected Drawable getCircularUserIcon() { + Drawable getCircularUserIcon() { Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier()); if (userIcon == null) { return null; diff --git a/src/com/android/settings/users/EditUserInfoController.java b/src/com/android/settings/users/EditUserInfoController.java new file mode 100644 index 000000000..235eef3bc --- /dev/null +++ b/src/com/android/settings/users/EditUserInfoController.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.users; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.Fragment; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.ImageView; + +import com.android.settings.R; + +public class EditUserInfoController { + private Dialog mEditUserInfoDialog; + private Bitmap mSavedPhoto; + private EditUserPhotoController mEditUserPhotoController; + private UserHandle mUser; + private UserManager mUserManager; + private boolean mWaitingForActivityResult = false; + + public interface OnContentChangedCallback { + public void onPhotoChanged(Drawable photo); + + public void onLabelChanged(CharSequence label); + } + + public void clear() { + mEditUserInfoDialog = null; + mSavedPhoto = null; + } + + public Dialog getDialog() { + return mEditUserInfoDialog; + } + + public void onRestoreInstanceState(Bundle icicle) { + mSavedPhoto = (Bitmap) icicle.getParcelable(RestrictedProfileSettings.KEY_SAVED_PHOTO); + } + + public void onSaveInstanceState(Bundle outState) { + if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() + && mEditUserPhotoController != null) { + outState.putParcelable(RestrictedProfileSettings.KEY_SAVED_PHOTO, + mEditUserPhotoController.getNewUserPhotoBitmap()); + } + if (mWaitingForActivityResult) { + outState.putBoolean(RestrictedProfileSettings.KEY_AWAITING_RESULT, + mWaitingForActivityResult); + } + } + + public void startingActivityForResult() { + mWaitingForActivityResult = true; + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + mWaitingForActivityResult = false; + + if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() + && mEditUserPhotoController.onActivityResult(requestCode, resultCode, data)) { + return; + } + } + + Drawable getCircularUserIcon(Activity activity) { + Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier()); + if (userIcon == null) { + return null; + } + CircleFramedDrawable circularIcon = + CircleFramedDrawable.getInstance(activity, userIcon); + return circularIcon; + } + + public Dialog createDialog(final Fragment fragment, final Drawable currentUserIcon, + final CharSequence currentUserName, + int titleResId, final OnContentChangedCallback callback, UserHandle user) { + Activity activity = fragment.getActivity(); + mUser = user; + if (mUserManager == null) { + mUserManager = UserManager.get(activity); + } + LayoutInflater inflater = activity.getLayoutInflater(); + View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); + + UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier()); + + final EditText userNameView = (EditText) content.findViewById(R.id.user_name); + userNameView.setText(info.name); + + final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo); + Drawable drawable = null; + if (mSavedPhoto != null) { + drawable = CircleFramedDrawable.getInstance(activity, + mSavedPhoto); + } else { + drawable = currentUserIcon; + if (drawable == null) { + drawable = getCircularUserIcon(activity); + } + } + userPhotoView.setImageDrawable(drawable); + mEditUserPhotoController = new EditUserPhotoController(fragment, userPhotoView, + mSavedPhoto, drawable, mWaitingForActivityResult); + mEditUserInfoDialog = new AlertDialog.Builder(activity) + .setTitle(R.string.profile_info_settings_title) + .setView(content) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Update the name if changed. + CharSequence userName = userNameView.getText(); + if (!TextUtils.isEmpty(userName)) { + if (currentUserName == null + || !userName.toString().equals(currentUserName.toString())) { + if (callback != null) { + callback.onLabelChanged(userName.toString()); + } + mUserManager.setUserName(mUser.getIdentifier(), + userName.toString()); + } + } + // Update the photo if changed. + Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable(); + Bitmap bitmap = mEditUserPhotoController.getNewUserPhotoBitmap(); + if (drawable != null && bitmap != null + && !drawable.equals(currentUserIcon)) { + if (callback != null) { + callback.onPhotoChanged(drawable); + } + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + mUserManager.setUserIcon(mUser.getIdentifier(), + mEditUserPhotoController.getNewUserPhotoBitmap()); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + fragment.getActivity().removeDialog( + RestrictedProfileSettings.DIALOG_ID_EDIT_USER_INFO); + } + clear(); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + clear(); + } + }) + .create(); + + // Make sure the IME is up. + mEditUserInfoDialog.getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + return mEditUserInfoDialog; + } +} diff --git a/src/com/android/settings/users/EditUserPhotoController.java b/src/com/android/settings/users/EditUserPhotoController.java new file mode 100644 index 000000000..5ed35609b --- /dev/null +++ b/src/com/android/settings/users/EditUserPhotoController.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.users; + +import android.app.Activity; +import android.app.Fragment; +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Bitmap.Config; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.MediaStore; +import android.provider.ContactsContract.DisplayPhoto; +import android.support.v4.content.FileProvider; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListAdapter; +import android.widget.ListPopupWindow; + +import com.android.settings.R; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class EditUserPhotoController { + private static final int POPUP_LIST_ITEM_ID_CHOOSE_PHOTO = 1; + private static final int POPUP_LIST_ITEM_ID_TAKE_PHOTO = 2; + + // It seems that this class generates custom request codes and they may + // collide with ours, these values are very unlikely to have a conflict. + private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001; + private static final int REQUEST_CODE_TAKE_PHOTO = 1002; + private static final int REQUEST_CODE_CROP_PHOTO = 1003; + + private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; + private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg"; + + private final int mPhotoSize; + + private final Context mContext; + private final Fragment mFragment; + private final ImageView mImageView; + + private final Uri mCropPictureUri; + private final Uri mTakePictureUri; + + private Bitmap mNewUserPhotoBitmap; + private Drawable mNewUserPhotoDrawable; + + public EditUserPhotoController(Fragment fragment, ImageView view, + Bitmap bitmap, Drawable drawable, boolean waiting) { + mContext = view.getContext(); + mFragment = fragment; + mImageView = view; + mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME, !waiting); + mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME, !waiting); + mPhotoSize = getPhotoSize(mContext); + mImageView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + showUpdatePhotoPopup(); + } + }); + mNewUserPhotoBitmap = bitmap; + mNewUserPhotoDrawable = drawable; + } + + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != Activity.RESULT_OK) { + return false; + } + final Uri pictureUri = data != null && data.getData() != null + ? data.getData() : mTakePictureUri; + switch (requestCode) { + case REQUEST_CODE_CROP_PHOTO: + onPhotoCropped(pictureUri, true); + return true; + case REQUEST_CODE_TAKE_PHOTO: + case REQUEST_CODE_CHOOSE_PHOTO: + cropPhoto(pictureUri); + return true; + } + return false; + } + + public Bitmap getNewUserPhotoBitmap() { + return mNewUserPhotoBitmap; + } + + public Drawable getNewUserPhotoDrawable() { + return mNewUserPhotoDrawable; + } + + private void showUpdatePhotoPopup() { + final boolean canTakePhoto = canTakePhoto(); + final boolean canChoosePhoto = canChoosePhoto(); + + if (!canTakePhoto && !canChoosePhoto) { + return; + } + + Context context = mImageView.getContext(); + final List<EditUserPhotoController.AdapterItem> items = new ArrayList<EditUserPhotoController.AdapterItem>(); + + if (canTakePhoto()) { + String title = mImageView.getContext().getString( R.string.user_image_take_photo); + EditUserPhotoController.AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_TAKE_PHOTO); + items.add(item); + } + + if (canChoosePhoto) { + String title = context.getString(R.string.user_image_choose_photo); + EditUserPhotoController.AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_CHOOSE_PHOTO); + items.add(item); + } + + final ListPopupWindow listPopupWindow = new ListPopupWindow(context); + + listPopupWindow.setAnchorView(mImageView); + listPopupWindow.setModal(true); + listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); + + ListAdapter adapter = new ArrayAdapter<EditUserPhotoController.AdapterItem>(context, + R.layout.edit_user_photo_popup_item, items); + listPopupWindow.setAdapter(adapter); + + final int width = Math.max(mImageView.getWidth(), context.getResources() + .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width)); + listPopupWindow.setWidth(width); + + listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + EditUserPhotoController.AdapterItem item = items.get(position); + switch (item.id) { + case POPUP_LIST_ITEM_ID_CHOOSE_PHOTO: { + choosePhoto(); + listPopupWindow.dismiss(); + } break; + case POPUP_LIST_ITEM_ID_TAKE_PHOTO: { + takePhoto(); + listPopupWindow.dismiss(); + } break; + } + } + }); + + listPopupWindow.show(); + } + + private boolean canTakePhoto() { + return mImageView.getContext().getPackageManager().queryIntentActivities( + new Intent(MediaStore.ACTION_IMAGE_CAPTURE), + PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + } + + private boolean canChoosePhoto() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + return mImageView.getContext().getPackageManager().queryIntentActivities( + intent, 0).size() > 0; + } + + private void takePhoto() { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + appendOutputExtra(intent, mTakePictureUri); + mFragment.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); + } + + private void choosePhoto() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); + intent.setType("image/*"); + appendOutputExtra(intent, mTakePictureUri); + mFragment.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); + } + + private void cropPhoto(Uri pictureUri) { + // TODO: Use a public intent, when there is one. + Intent intent = new Intent("com.android.camera.action.CROP"); + intent.setDataAndType(pictureUri, "image/*"); + appendOutputExtra(intent, mCropPictureUri); + appendCropExtras(intent); + if (intent.resolveActivity(mContext.getPackageManager()) != null) { + mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); + } else { + onPhotoCropped(pictureUri, false); + } + } + + private void appendOutputExtra(Intent intent, Uri pictureUri) { + intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri)); + } + + private void appendCropExtras(Intent intent) { + intent.putExtra("crop", "true"); + intent.putExtra("scale", true); + intent.putExtra("scaleUpIfNeeded", true); + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + intent.putExtra("outputX", mPhotoSize); + intent.putExtra("outputY", mPhotoSize); + } + + private void onPhotoCropped(final Uri data, final boolean cropped) { + new AsyncTask<Void, Void, Bitmap>() { + @Override + protected Bitmap doInBackground(Void... params) { + if (cropped) { + try { + InputStream imageStream = mContext.getContentResolver() + .openInputStream(data); + return BitmapFactory.decodeStream(imageStream); + } catch (FileNotFoundException fe) { + return null; + } + } else { + // Scale and crop to a square aspect ratio + Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize, + Config.ARGB_8888); + Canvas canvas = new Canvas(croppedImage); + Bitmap fullImage = null; + try { + InputStream imageStream = mContext.getContentResolver() + .openInputStream(data); + fullImage = BitmapFactory.decodeStream(imageStream); + } catch (FileNotFoundException fe) { + return null; + } + if (fullImage != null) { + final int squareSize = Math.min(fullImage.getWidth(), + fullImage.getHeight()); + final int left = (fullImage.getWidth() - squareSize) / 2; + final int top = (fullImage.getHeight() - squareSize) / 2; + Rect rectSource = new Rect(left, top, + left + squareSize, top + squareSize); + Rect rectDest = new Rect(0, 0, mPhotoSize, mPhotoSize); + Paint paint = new Paint(); + canvas.drawBitmap(fullImage, rectSource, rectDest, paint); + return croppedImage; + } else { + // Bah! Got nothin. + return null; + } + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null) { + mNewUserPhotoBitmap = bitmap; + mNewUserPhotoDrawable = CircleFramedDrawable + .getInstance(mImageView.getContext(), mNewUserPhotoBitmap); + mImageView.setImageDrawable(mNewUserPhotoDrawable); + } + new File(mContext.getCacheDir(), TAKE_PICTURE_FILE_NAME).delete(); + new File(mContext.getCacheDir(), CROP_PICTURE_FILE_NAME).delete(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + + private static int getPhotoSize(Context context) { + Cursor cursor = context.getContentResolver().query( + DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, + new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); + try { + cursor.moveToFirst(); + return cursor.getInt(0); + } finally { + cursor.close(); + } + } + + private Uri createTempImageUri(Context context, String fileName, boolean purge) { + final File folder = context.getCacheDir(); + folder.mkdirs(); + final File fullPath = new File(folder, fileName); + if (purge) { + fullPath.delete(); + } + final Uri fileUri = + FileProvider.getUriForFile(context, RestrictedProfileSettings.FILE_PROVIDER_AUTHORITY, fullPath); + return fileUri; + } + + private static final class AdapterItem { + final String title; + final int id; + + public AdapterItem(String title, int id) { + this.title = title; + this.id = id; + } + + @Override + public String toString() { + return title; + } + } +}
\ No newline at end of file diff --git a/src/com/android/settings/users/RestrictedProfileSettings.java b/src/com/android/settings/users/RestrictedProfileSettings.java index ce96c3d56..3915ef21b 100644 --- a/src/com/android/settings/users/RestrictedProfileSettings.java +++ b/src/com/android/settings/users/RestrictedProfileSettings.java @@ -16,66 +16,35 @@ package com.android.settings.users; -import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; -import android.app.Fragment; -import android.content.ClipData; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.content.pm.UserInfo; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; -import android.provider.MediaStore; -import android.provider.ContactsContract.DisplayPhoto; -import android.support.v4.content.FileProvider; -import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; -import android.view.View.OnClickListener; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.EditText; import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.ListPopupWindow; import android.widget.TextView; import com.android.settings.R; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; import java.util.List; -public class RestrictedProfileSettings extends AppRestrictionsFragment { +public class RestrictedProfileSettings extends AppRestrictionsFragment + implements EditUserInfoController.OnContentChangedCallback { - private static final String KEY_SAVED_PHOTO = "pending_photo"; - private static final String KEY_AWAITING_RESULT = "awaiting_result"; - private static final int DIALOG_ID_EDIT_USER_INFO = 1; + static final String KEY_SAVED_PHOTO = "pending_photo"; + static final String KEY_AWAITING_RESULT = "awaiting_result"; + static final int DIALOG_ID_EDIT_USER_INFO = 1; public static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files"; private View mHeaderView; private ImageView mUserIconView; private TextView mUserNameView; - private Dialog mEditUserInfoDialog; - private EditUserPhotoController mEditUserPhotoController; - private Bitmap mSavedPhoto; + private EditUserInfoController mEditUserInfoController = + new EditUserInfoController(); private boolean mWaitingForActivityResult; @Override @@ -83,7 +52,7 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment { super.onCreate(icicle); if (icicle != null) { - mSavedPhoto = (Bitmap) icicle.getParcelable(KEY_SAVED_PHOTO); + mEditUserInfoController.onRestoreInstanceState(icicle); mWaitingForActivityResult = icicle.getBoolean(KEY_AWAITING_RESULT, false); } @@ -108,14 +77,7 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment { @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() - && mEditUserPhotoController != null) { - outState.putParcelable(KEY_SAVED_PHOTO, - mEditUserPhotoController.getNewUserPhotoBitmap()); - } - if (mWaitingForActivityResult) { - outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult); - } + mEditUserInfoController.onSaveInstanceState(outState); } @Override @@ -145,19 +107,15 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment { @Override public void startActivityForResult(Intent intent, int requestCode) { - mWaitingForActivityResult = true; + mEditUserInfoController.startingActivityForResult(); super.startActivityForResult(intent, requestCode); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - mWaitingForActivityResult = false; - if (mEditUserInfoDialog != null && mEditUserInfoDialog.isShowing() - && mEditUserPhotoController.onActivityResult(requestCode, resultCode, data)) { - return; - } + mEditUserInfoController.onActivityResult(requestCode, resultCode, data); } @Override @@ -172,372 +130,21 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment { @Override public Dialog onCreateDialog(int dialogId) { if (dialogId == DIALOG_ID_EDIT_USER_INFO) { - if (mEditUserInfoDialog != null) { - return mEditUserInfoDialog; - } - - LayoutInflater inflater = getActivity().getLayoutInflater(); - View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); - - UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier()); - - final EditText userNameView = (EditText) content.findViewById(R.id.user_name); - userNameView.setText(info.name); - - final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo); - Drawable drawable = null; - if (mSavedPhoto != null) { - drawable = CircleFramedDrawable.getInstance(getActivity(), mSavedPhoto); - } else { - drawable = mUserIconView.getDrawable(); - if (drawable == null) { - drawable = getCircularUserIcon(); - } - } - userPhotoView.setImageDrawable(drawable); - mEditUserPhotoController = new EditUserPhotoController(this, userPhotoView, - mSavedPhoto, drawable, mWaitingForActivityResult); - - mEditUserInfoDialog = new AlertDialog.Builder(getActivity()) - .setTitle(R.string.profile_info_settings_title) - .setView(content) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { - // Update the name if changed. - CharSequence userName = userNameView.getText(); - if (!TextUtils.isEmpty(userName)) { - CharSequence oldUserName = mUserNameView.getText(); - if (oldUserName == null - || !userName.toString().equals(oldUserName.toString())) { - ((TextView) mHeaderView.findViewById(android.R.id.title)) - .setText(userName.toString()); - mUserManager.setUserName(mUser.getIdentifier(), - userName.toString()); - } - } - // Update the photo if changed. - Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable(); - Bitmap bitmap = mEditUserPhotoController.getNewUserPhotoBitmap(); - if (drawable != null && bitmap != null - && !drawable.equals(mUserIconView.getDrawable())) { - mUserIconView.setImageDrawable(drawable); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - mUserManager.setUserIcon(mUser.getIdentifier(), - mEditUserPhotoController.getNewUserPhotoBitmap()); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); - } - removeDialog(DIALOG_ID_EDIT_USER_INFO); - } - clearEditUserInfoDialog(); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - clearEditUserInfoDialog(); - } - }) - .create(); - - // Make sure the IME is up. - mEditUserInfoDialog.getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - return mEditUserInfoDialog; + return mEditUserInfoController.createDialog(this, mUserIconView.getDrawable(), + mUserNameView.getText(), R.string.profile_info_settings_title, + this, mUser); } return null; } - private void clearEditUserInfoDialog() { - mEditUserInfoDialog = null; - mSavedPhoto = null; + @Override + public void onPhotoChanged(Drawable photo) { + mUserIconView.setImageDrawable(photo); } - private static class EditUserPhotoController { - private static final int POPUP_LIST_ITEM_ID_CHOOSE_PHOTO = 1; - private static final int POPUP_LIST_ITEM_ID_TAKE_PHOTO = 2; - - // It seems that this class generates custom request codes and they may - // collide with ours, these values are very unlikely to have a conflict. - private static final int REQUEST_CODE_CHOOSE_PHOTO = 1; - private static final int REQUEST_CODE_TAKE_PHOTO = 2; - private static final int REQUEST_CODE_CROP_PHOTO = 3; - - private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; - private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg"; - - private final int mPhotoSize; - - private final Context mContext; - private final Fragment mFragment; - private final ImageView mImageView; - - private final Uri mCropPictureUri; - private final Uri mTakePictureUri; - - private Bitmap mNewUserPhotoBitmap; - private Drawable mNewUserPhotoDrawable; - - public EditUserPhotoController(Fragment fragment, ImageView view, - Bitmap bitmap, Drawable drawable, boolean waiting) { - mContext = view.getContext(); - mFragment = fragment; - mImageView = view; - mCropPictureUri = createTempImageUri(mContext, CROP_PICTURE_FILE_NAME, !waiting); - mTakePictureUri = createTempImageUri(mContext, TAKE_PICTURE_FILE_NAME, !waiting); - mPhotoSize = getPhotoSize(mContext); - mImageView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - showUpdatePhotoPopup(); - } - }); - mNewUserPhotoBitmap = bitmap; - mNewUserPhotoDrawable = drawable; - } - - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode != Activity.RESULT_OK) { - return false; - } - final Uri pictureUri = data != null && data.getData() != null - ? data.getData() : mTakePictureUri; - switch (requestCode) { - case REQUEST_CODE_CROP_PHOTO: - onPhotoCropped(pictureUri, true); - return true; - case REQUEST_CODE_TAKE_PHOTO: - case REQUEST_CODE_CHOOSE_PHOTO: - cropPhoto(pictureUri); - return true; - } - return false; - } - - public Bitmap getNewUserPhotoBitmap() { - return mNewUserPhotoBitmap; - } - - public Drawable getNewUserPhotoDrawable() { - return mNewUserPhotoDrawable; - } - - private void showUpdatePhotoPopup() { - final boolean canTakePhoto = canTakePhoto(); - final boolean canChoosePhoto = canChoosePhoto(); - - if (!canTakePhoto && !canChoosePhoto) { - return; - } - - Context context = mImageView.getContext(); - final List<AdapterItem> items = new ArrayList<AdapterItem>(); - - if (canTakePhoto()) { - String title = mImageView.getContext().getString( R.string.user_image_take_photo); - AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_TAKE_PHOTO); - items.add(item); - } - - if (canChoosePhoto) { - String title = context.getString(R.string.user_image_choose_photo); - AdapterItem item = new AdapterItem(title, POPUP_LIST_ITEM_ID_CHOOSE_PHOTO); - items.add(item); - } - - final ListPopupWindow listPopupWindow = new ListPopupWindow(context); - - listPopupWindow.setAnchorView(mImageView); - listPopupWindow.setModal(true); - listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); - - ListAdapter adapter = new ArrayAdapter<AdapterItem>(context, - R.layout.edit_user_photo_popup_item, items); - listPopupWindow.setAdapter(adapter); - - final int width = Math.max(mImageView.getWidth(), context.getResources() - .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width)); - listPopupWindow.setWidth(width); - - listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - AdapterItem item = items.get(position); - switch (item.id) { - case POPUP_LIST_ITEM_ID_CHOOSE_PHOTO: { - choosePhoto(); - listPopupWindow.dismiss(); - } break; - case POPUP_LIST_ITEM_ID_TAKE_PHOTO: { - takePhoto(); - listPopupWindow.dismiss(); - } break; - } - } - }); - - listPopupWindow.show(); - } - - private boolean canTakePhoto() { - return mImageView.getContext().getPackageManager().queryIntentActivities( - new Intent(MediaStore.ACTION_IMAGE_CAPTURE), - PackageManager.MATCH_DEFAULT_ONLY).size() > 0; - } - - private boolean canChoosePhoto() { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("image/*"); - return mImageView.getContext().getPackageManager().queryIntentActivities( - intent, 0).size() > 0; - } - - private void takePhoto() { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - appendOutputExtra(intent, mTakePictureUri); - mFragment.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); - } - - private void choosePhoto() { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); - intent.setType("image/*"); - appendOutputExtra(intent, mTakePictureUri); - mFragment.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); - } - - private void cropPhoto(Uri pictureUri) { - // TODO: Use a public intent, when there is one. - Intent intent = new Intent("com.android.camera.action.CROP"); - intent.setDataAndType(pictureUri, "image/*"); - appendOutputExtra(intent, mCropPictureUri); - appendCropExtras(intent); - if (intent.resolveActivity(mContext.getPackageManager()) != null) { - mFragment.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); - } else { - onPhotoCropped(pictureUri, false); - } - } - - private void appendOutputExtra(Intent intent, Uri pictureUri) { - intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri); - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri)); - } - - private void appendCropExtras(Intent intent) { - intent.putExtra("crop", "true"); - intent.putExtra("scale", true); - intent.putExtra("scaleUpIfNeeded", true); - intent.putExtra("aspectX", 1); - intent.putExtra("aspectY", 1); - intent.putExtra("outputX", mPhotoSize); - intent.putExtra("outputY", mPhotoSize); - } - - private void onPhotoCropped(final Uri data, final boolean cropped) { - new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... params) { - if (cropped) { - try { - InputStream imageStream = mContext.getContentResolver() - .openInputStream(data); - return BitmapFactory.decodeStream(imageStream); - } catch (FileNotFoundException fe) { - return null; - } - } else { - // Scale and crop to a square aspect ratio - Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize, - Config.ARGB_8888); - Canvas canvas = new Canvas(croppedImage); - Bitmap fullImage = null; - try { - InputStream imageStream = mContext.getContentResolver() - .openInputStream(data); - fullImage = BitmapFactory.decodeStream(imageStream); - } catch (FileNotFoundException fe) { - return null; - } - if (fullImage != null) { - final int squareSize = Math.min(fullImage.getWidth(), - fullImage.getHeight()); - final int left = (fullImage.getWidth() - squareSize) / 2; - final int top = (fullImage.getHeight() - squareSize) / 2; - Rect rectSource = new Rect(left, top, - left + squareSize, top + squareSize); - Rect rectDest = new Rect(0, 0, mPhotoSize, mPhotoSize); - Paint paint = new Paint(); - canvas.drawBitmap(fullImage, rectSource, rectDest, paint); - return croppedImage; - } else { - // Bah! Got nothin. - return null; - } - } - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null) { - mNewUserPhotoBitmap = bitmap; - mNewUserPhotoDrawable = CircleFramedDrawable - .getInstance(mImageView.getContext(), mNewUserPhotoBitmap); - mImageView.setImageDrawable(mNewUserPhotoDrawable); - } - new File(mContext.getCacheDir(), TAKE_PICTURE_FILE_NAME).delete(); - new File(mContext.getCacheDir(), CROP_PICTURE_FILE_NAME).delete(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); - } - - private static int getPhotoSize(Context context) { - Cursor cursor = context.getContentResolver().query( - DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, - new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null); - try { - cursor.moveToFirst(); - return cursor.getInt(0); - } finally { - cursor.close(); - } - } - - private Uri createTempImageUri(Context context, String fileName, boolean purge) { - final File folder = context.getCacheDir(); - folder.mkdirs(); - final File fullPath = new File(folder, fileName); - if (purge) { - fullPath.delete(); - } - final Uri fileUri = - FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, fullPath); - return fileUri; - } - - private static final class AdapterItem { - final String title; - final int id; - - public AdapterItem(String title, int id) { - this.title = title; - this.id = id; - } - - @Override - public String toString() { - return title; - } - } + @Override + public void onLabelChanged(CharSequence label) { + mUserNameView.setText(label); } - } diff --git a/src/com/android/settings/users/UserPreference.java b/src/com/android/settings/users/UserPreference.java index 72aa9a4c5..23359ecc2 100644 --- a/src/com/android/settings/users/UserPreference.java +++ b/src/com/android/settings/users/UserPreference.java @@ -16,7 +16,6 @@ package com.android.settings.users; -import com.android.internal.util.CharSequences; import com.android.settings.R; import android.content.Context; @@ -36,8 +35,6 @@ public class UserPreference extends Preference { private OnClickListener mSettingsClickListener; private int mSerialNumber = -1; private int mUserId = USERID_UNKNOWN; - private boolean mRestricted; - private boolean mSelf; static final int SETTINGS_ID = R.id.manage_user; static final int DELETE_ID = R.id.trash_user; diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index e8c99ad4e..5f3c1f8c5 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -26,6 +26,7 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.app.Dialog; +import android.app.Fragment; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -70,9 +71,18 @@ import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; +/** + * Screen that manages the list of users on the device. + * Guest user is an always visible entry, even if the guest is not currently + * active/created. It is meant for controlling properties of a guest user. + * + * The first one is always the current user. + * Owner is the primary user. + */ public class UserSettings extends SettingsPreferenceFragment implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener, - Preference.OnPreferenceChangeListener { + Preference.OnPreferenceChangeListener, + EditUserInfoController.OnContentChangedCallback { private static final String TAG = "UserSettings"; @@ -95,6 +105,7 @@ public class UserSettings extends SettingsPreferenceFragment private static final int DIALOG_CHOOSE_USER_TYPE = 6; private static final int DIALOG_NEED_LOCKSCREEN = 7; private static final int DIALOG_CONFIRM_EXIT_GUEST = 8; + private static final int DIALOG_USER_PROFILE_EDITOR = 9; private static final int MESSAGE_UPDATE_LIST = 1; private static final int MESSAGE_SETUP_USER = 2; @@ -141,6 +152,9 @@ public class UserSettings extends SettingsPreferenceFragment private boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER; private boolean mIsGuest; + private EditUserInfoController mEditUserInfoController = + new EditUserInfoController(); + // A place to cache the generated guest avatar private Drawable mGuestDrawable; // A place to cache the generated default avatar @@ -189,6 +203,7 @@ public class UserSettings extends SettingsPreferenceFragment if (icicle.containsKey(SAVE_REMOVING_USER)) { mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER); } + mEditUserInfoController.onRestoreInstanceState(icicle); } final Context context = getActivity(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); @@ -205,7 +220,7 @@ public class UserSettings extends SettingsPreferenceFragment addPreferencesFromResource(R.xml.user_settings); mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST); mMePreference = new UserPreference(context, null /* attrs */, myUserId, - mUserManager.isLinkedUser() || mIsGuest ? null : this /* settings icon handler */, + null /* settings icon handler */, null /* delete icon handler */); mMePreference.setKey(KEY_USER_ME); mMePreference.setOnPreferenceClickListener(this); @@ -257,12 +272,18 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - + mEditUserInfoController.onSaveInstanceState(outState); outState.putInt(SAVE_ADDING_USER, mAddedUserId); outState.putInt(SAVE_REMOVING_USER, mRemovingUserId); } @Override + public void startActivityForResult(Intent intent, int requestCode) { + mEditUserInfoController.startingActivityForResult(); + super.startActivityForResult(intent, requestCode); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); if (!mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)) { @@ -349,6 +370,8 @@ public class UserSettings extends SettingsPreferenceFragment if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) { addUserNow(USER_TYPE_RESTRICTED_PROFILE); } + } else { + mEditUserInfoController.onActivityResult(requestCode, resultCode, data); } } @@ -610,6 +633,16 @@ public class UserSettings extends SettingsPreferenceFragment .create(); return dlg; } + case DIALOG_USER_PROFILE_EDITOR: { + Dialog dlg = mEditUserInfoController.createDialog( + (Fragment) this, + mMePreference.getIcon(), + mMePreference.getTitle(), + R.string.profile_info_settings_title, + this /* callback */, + android.os.Process.myUserHandle()); + return dlg; + } default: return null; } @@ -859,23 +892,11 @@ public class UserSettings extends SettingsPreferenceFragment showDialog(DIALOG_CONFIRM_EXIT_GUEST); return true; } - Intent editProfile; - if (!mProfileExists) { - editProfile = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); - // TODO: Make this a proper API - editProfile.putExtra("newLocalProfile", true); - } else { - editProfile = new Intent(Intent.ACTION_EDIT, ContactsContract.Profile.CONTENT_URI); - } - // To make sure that it returns back here when done - // TODO: Make this a proper API - editProfile.putExtra("finishActivityOnSaveCompleted", true); - // If this is a limited user, launch the user info settings instead of profile editor if (mUserManager.isLinkedUser()) { onManageUserClicked(UserHandle.myUserId(), false); } else { - startActivity(editProfile); + showDialog(DIALOG_USER_PROFILE_EDITOR); } } else if (pref instanceof UserPreference) { int userId = ((UserPreference) pref).getUserId(); @@ -974,4 +995,14 @@ public class UserSettings extends SettingsPreferenceFragment public int getHelpResource() { return R.string.help_url_users; } + + @Override + public void onPhotoChanged(Drawable photo) { + mMePreference.setIcon(photo); + } + + @Override + public void onLabelChanged(CharSequence label) { + mMePreference.setTitle(label); + } } |