blob: 58599532d9cb74dcb6336bd23e0b868a911dd0e0 [file] [log] [blame]
/*
* 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.settingslib.users;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.R;
import com.android.settingslib.drawable.CircleFramedDrawable;
import java.io.File;
import java.util.function.BiConsumer;
/**
* This class encapsulates a Dialog for editing the user nickname and photo.
*/
public class EditUserInfoController {
private static final String KEY_AWAITING_RESULT = "awaiting_result";
private static final String KEY_SAVED_PHOTO = "pending_photo";
private Dialog mEditUserInfoDialog;
private Bitmap mSavedPhoto;
private EditUserPhotoController mEditUserPhotoController;
private boolean mWaitingForActivityResult = false;
private final String mFileAuthority;
public EditUserInfoController(String fileAuthority) {
mFileAuthority = fileAuthority;
}
private void clear() {
if (mEditUserPhotoController != null) {
mEditUserPhotoController.removeNewUserPhotoBitmapFile();
}
mEditUserInfoDialog = null;
mSavedPhoto = null;
}
/**
* This should be called when the container activity/fragment got re-initialized from a
* previously saved state.
*/
public void onRestoreInstanceState(Bundle icicle) {
String pendingPhoto = icicle.getString(KEY_SAVED_PHOTO);
if (pendingPhoto != null) {
mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(new File(pendingPhoto));
}
mWaitingForActivityResult = icicle.getBoolean(KEY_AWAITING_RESULT, false);
}
/**
* Should be called from the container activity/fragment when it's onSaveInstanceState is
* called.
*/
public void onSaveInstanceState(Bundle outState) {
if (mEditUserInfoDialog != null && mEditUserPhotoController != null) {
// Bitmap cannot be stored into bundle because it may exceed parcel limit
// Store it in a temporary file instead
File file = mEditUserPhotoController.saveNewUserPhotoBitmap();
if (file != null) {
outState.putString(KEY_SAVED_PHOTO, file.getPath());
}
}
outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
}
/**
* Should be called from the container activity/fragment when an activity has started for
* take/choose/crop photo actions.
*/
public void startingActivityForResult() {
mWaitingForActivityResult = true;
}
/**
* Should be called from the container activity/fragment after it receives a result from
* take/choose/crop photo activity.
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mWaitingForActivityResult = false;
if (mEditUserPhotoController != null && mEditUserInfoDialog != null) {
mEditUserPhotoController.onActivityResult(requestCode, resultCode, data);
}
}
/**
* Creates a user edit dialog with option to change the user's name and photo.
*
* @param activityStarter - ActivityStarter is called with appropriate intents and request
* codes to take photo/choose photo/crop photo.
*/
public Dialog createDialog(Activity activity, ActivityStarter activityStarter,
@Nullable Drawable oldUserIcon, String defaultUserName, String title,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
LayoutInflater inflater = LayoutInflater.from(activity);
View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);
EditText userNameView = content.findViewById(R.id.user_name);
userNameView.setText(defaultUserName);
ImageView userPhotoView = content.findViewById(R.id.user_photo);
// if oldUserIcon param is null then we use a default gray user icon
Drawable defaultUserIcon = oldUserIcon != null ? oldUserIcon : UserIcons.getDefaultUserIcon(
activity.getResources(), UserHandle.USER_NULL, false);
// in case a new photo was selected and the activity got recreated we have to load the image
Drawable userIcon = getUserIcon(activity, defaultUserIcon);
userPhotoView.setImageDrawable(userIcon);
if (canChangePhoto(activity)) {
mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
userPhotoView);
} else {
// some users can't change their photos so we need to remove suggestive
// background from the photoView
userPhotoView.setBackground(null);
}
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
defaultUserName, title, successCallback, cancelCallback);
// Make sure the IME is up.
mEditUserInfoDialog.getWindow()
.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
return mEditUserInfoDialog;
}
private Drawable getUserIcon(Activity activity, Drawable defaultUserIcon) {
if (mSavedPhoto != null) {
return CircleFramedDrawable.getInstance(activity, mSavedPhoto);
}
return defaultUserIcon;
}
private Dialog buildDialog(Activity activity, View content, EditText userNameView,
@Nullable Drawable oldUserIcon, String defaultUserName, String title,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
return new AlertDialog.Builder(activity)
.setTitle(title)
.setView(content)
.setCancelable(true)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Drawable newUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
Drawable userIcon = newUserIcon != null
? newUserIcon
: oldUserIcon;
String newName = userNameView.getText().toString().trim();
String userName = !newName.isEmpty() ? newName : defaultUserName;
clear();
if (successCallback != null) {
successCallback.accept(userName, userIcon);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
}
})
.setOnCancelListener(dialog -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
}
})
.create();
}
@VisibleForTesting
boolean canChangePhoto(Context context) {
return (PhotoCapabilityUtils.canCropPhoto(context)
&& PhotoCapabilityUtils.canChoosePhoto(context))
|| PhotoCapabilityUtils.canTakePhoto(context);
}
@VisibleForTesting
EditUserPhotoController createEditUserPhotoController(Activity activity,
ActivityStarter activityStarter, ImageView userPhotoView) {
return new EditUserPhotoController(activity, activityStarter, userPhotoView,
mSavedPhoto, mWaitingForActivityResult, mFileAuthority);
}
}