Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ac45dd4 authored by Andras Kloczl's avatar Andras Kloczl
Browse files

Add user name photo dialog to user creation in SysUI

- Move user creation dialog related resources to SettingsLib
- Change dialog showing logic in UserSettings because
   EditUserInfoController contracts have changed.
- Show UserCreatingDialog when user is being created
- Fix crash when phone is rotated during user creation

Test: manual test
Doc: http://shortn/_cJE9o6pBZR
Screenrecord: http://shortn/_Jy5Q0lTAUL
Bug: 147653252
Change-Id: I15e15ad88b768a5b679de32c5429d921d850a3cb
parent 0488650a
Loading
Loading
Loading
Loading
+0 −49
Original line number Diff line number Diff line
<!--
     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.
-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:baselineAligned="false"
    android:padding="16dip">

    <ImageView
        android:id="@+id/user_photo"
        android:layout_width="56dip"
        android:layout_height="56dip"
        android:layout_gravity="bottom"
        android:contentDescription="@string/user_image_photo_selector"
        android:background="@*android:drawable/spinner_background_holo_dark"
        android:scaleType="fitCenter"/>

    <EditText
        android:id="@+id/user_name"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_weight="1"
        android:layout_marginStart="6dp"
        android:minHeight="@dimen/min_tap_target_size"
        android:ellipsize="end"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textAlignment="viewStart"
        android:inputType="text|textCapWords"
        android:selectAllOnFocus="true"
        android:hint="@string/user_nickname"
        android:maxLength="100"/>

</LinearLayout>
+0 −44
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2016, 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.
*/
-->

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:gravity="center_vertical"
    android:paddingStart="16dip"
    android:paddingEnd="16dip">
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="?android:attr/textColorAlertDialogListItem"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:ellipsize="marquee"
        android:layout_alignParentLeft="true" />
    <ImageView
        android:id="@+id/restricted_icon"
        android:layout_width="@*android:dimen/config_restrictedIconSize"
        android:layout_height="@*android:dimen/config_restrictedIconSize"
        android:scaleType="centerInside"
        android:tint="?android:attr/colorAccent"
        android:src="@*android:drawable/ic_info"
        android:layout_alignParentRight="true"
        android:visibility="gone" />
</RelativeLayout>
+0 −3
Original line number Diff line number Diff line
@@ -51,9 +51,6 @@
    <dimen name="pager_tabs_title_padding">16dp</dimen>
    <dimen name="pager_tabs_selected_indicator_height">3dp</dimen>

    <!-- Minimum width for the popup for updating a user's photo. -->
    <dimen name="update_user_photo_popup_min_width">300dip</dimen>

    <dimen name="captioning_preview_height">200dp</dimen>

    <dimen name="autoclick_preview_height">200dp</dimen>
+0 −9
Original line number Diff line number Diff line
@@ -6990,8 +6990,6 @@
    <string name="user_admin">Admin</string>
    <!-- User settings title for current user entry "You" user. [CHAR LIMIT=30] -->
    <string name="user_you">You (<xliff:g id="name" example="Name">%s</xliff:g>)</string>
    <!-- Title for the preference to enter the nickname of the userto display in the user switcher [CHAR LIMIT=25]-->
    <string name="user_nickname">Nickname</string>
    <!-- Summary for add user action, when it's disabled [CHAR LIMIT=100] -->
    <string name="user_add_max_count">You can add up to <xliff:g id="user_count">%1$d</xliff:g> users</string>
@@ -7429,13 +7427,6 @@
    <!-- Wizard finish button label [CHAR LIMIT=25] -->
    <string name="wizard_finish">Finish</string>
    <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
    <string name="user_image_take_photo" msgid="7496128293167402354">Take a photo</string>
    <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
    <string name="user_image_choose_photo" msgid="3746334626214970837">Choose an image</string>
    <!-- Accessibility message for the photo selector which is a button/popup with the current photo [CHAR LIMIT=50] -->
    <string name="user_image_photo_selector">Select photo</string>
    <!-- Text to display in regulatory info screen (from device overlay). -->
    <string name="regulatory_info_text"></string>
+0 −238
Original line number Diff line number Diff line
/*
 * 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.Dialog;
import android.content.Context;
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.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 androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;

import com.android.settings.R;
import com.android.settingslib.drawable.CircleFramedDrawable;

import java.io.File;

/**
 * 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 UserHandle mUser;
    private UserManager mUserManager;
    private boolean mWaitingForActivityResult = false;

    /**
     * Callback made when either the username text or photo choice changes.
     */
    public interface OnContentChangedCallback {
        /** Photo updated. */
        void onPhotoChanged(UserHandle user, Drawable photo);
        /** Username updated. */
        void onLabelChanged(UserHandle user, CharSequence label);
    }

    /**
     * Callback made when the dialog finishes.
     */
    public interface OnDialogCompleteCallback {
        /** Dialog closed with positive button. */
        void onPositive();
        /** Dialog closed with negative button or cancelled. */
        void onNegativeOrCancel();
    }

    public void clear() {
        if (mEditUserPhotoController != null) {
            mEditUserPhotoController.removeNewUserPhotoBitmapFile();
        }
        mEditUserInfoDialog = null;
        mSavedPhoto = null;
    }

    public Dialog getDialog() {
        return mEditUserInfoDialog;
    }

    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);
    }

    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());
            }
        }
        if (mWaitingForActivityResult) {
            outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
        }
    }

    public void startingActivityForResult() {
        mWaitingForActivityResult = true;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        mWaitingForActivityResult = false;

        if (mEditUserPhotoController != null && mEditUserInfoDialog != null) {
            mEditUserPhotoController.onActivityResult(requestCode, resultCode, data);
        }
    }

    public Dialog createDialog(final Fragment fragment, final Drawable currentUserIcon,
            final CharSequence currentUserName,
            String title, final OnContentChangedCallback callback, UserHandle user,
            OnDialogCompleteCallback completeCallback) {
        Activity activity = fragment.getActivity();
        mUser = user;
        if (mUserManager == null) {
            mUserManager = activity.getSystemService(UserManager.class);
        }
        LayoutInflater inflater = activity.getLayoutInflater();
        View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);

        final EditText userNameView = (EditText) content.findViewById(R.id.user_name);
        userNameView.setText(currentUserName);

        final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo);

        boolean canChangePhoto = mUserManager != null &&
                canChangePhoto(activity, mUserManager.getUserInfo(user.getIdentifier()));
        if (!canChangePhoto) {
            // some users can't change their photos so we need to remove suggestive
            // background from the photoView
            userPhotoView.setBackground(null);
        }
        Drawable drawable = null;
        if (mSavedPhoto != null) {
            drawable = CircleFramedDrawable.getInstance(activity, mSavedPhoto);
        } else {
            drawable = currentUserIcon;
        }
        userPhotoView.setImageDrawable(drawable);
        if (canChangePhoto) {
            mEditUserPhotoController =
                    createEditUserPhotoController(fragment, userPhotoView, drawable);
        }
        mEditUserInfoDialog = new AlertDialog.Builder(activity)
                .setTitle(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(mUser, userName.toString());
                                    }
                                }
                            }
                            // Update the photo if changed.
                            if (mEditUserPhotoController != null) {
                                Drawable drawable =
                                        mEditUserPhotoController.getNewUserPhotoDrawable();
                                if (drawable != null && !drawable.equals(currentUserIcon)) {
                                    if (callback != null) {
                                        callback.onPhotoChanged(mUser, drawable);
                                    }
                                }
                            }
                        }
                        clear();
                        if (completeCallback != null) {
                            completeCallback.onPositive();
                        }
                    }
                })
                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        clear();
                        if (completeCallback != null) {
                            completeCallback.onNegativeOrCancel();
                        }
                    }
                })
                .setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        clear();
                        if (completeCallback != null) {
                            completeCallback.onNegativeOrCancel();
                        }
                    }
                })
                .create();

        // Make sure the IME is up.
        mEditUserInfoDialog.getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);

        return mEditUserInfoDialog;
    }

    @VisibleForTesting
    boolean canChangePhoto(Context context, UserInfo user) {
        return PhotoCapabilityUtils.canCropPhoto(context) &&
                (PhotoCapabilityUtils.canChoosePhoto(context)
                        || PhotoCapabilityUtils.canTakePhoto(context));
    }

    @VisibleForTesting
    EditUserPhotoController createEditUserPhotoController(Fragment fragment,
            ImageView userPhotoView, Drawable drawable) {
        return new EditUserPhotoController(fragment, userPhotoView,
                mSavedPhoto, drawable, mWaitingForActivityResult);
    }
}
Loading