Merge changes from topic "single_dialog" into udc-dev
* changes: Update user creation dialog to happen in a single dialog Create single dialog for adding a user.
This commit is contained in:
committed by
Android (Google) Code Review
commit
0624d822d4
25
packages/SettingsLib/res/drawable/ic_person_add.xml
Normal file
25
packages/SettingsLib/res/drawable/ic_person_add.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?android:attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18,14V11H15V9H18V6H20V9H23V11H20V14ZM9,12Q7.35,12 6.175,10.825Q5,9.65 5,8Q5,6.35 6.175,5.175Q7.35,4 9,4Q10.65,4 11.825,5.175Q13,6.35 13,8Q13,9.65 11.825,10.825Q10.65,12 9,12ZM1,20V17.2Q1,16.35 1.438,15.637Q1.875,14.925 2.6,14.55Q4.15,13.775 5.75,13.387Q7.35,13 9,13Q10.65,13 12.25,13.387Q13.85,13.775 15.4,14.55Q16.125,14.925 16.562,15.637Q17,16.35 17,17.2V20ZM3,18H15V17.2Q15,16.925 14.863,16.7Q14.725,16.475 14.5,16.35Q13.15,15.675 11.775,15.337Q10.4,15 9,15Q7.6,15 6.225,15.337Q4.85,15.675 3.5,16.35Q3.275,16.475 3.138,16.7Q3,16.925 3,17.2ZM9,10Q9.825,10 10.413,9.412Q11,8.825 11,8Q11,7.175 10.413,6.588Q9.825,6 9,6Q8.175,6 7.588,6.588Q7,7.175 7,8Q7,8.825 7.588,9.412Q8.175,10 9,10ZM9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8Q9,8 9,8ZM9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Q9,18 9,18Z"/>
|
||||||
|
</vector>
|
||||||
@@ -13,50 +13,53 @@
|
|||||||
~ See the License for the specific language governing permissions and
|
~ See the License for the specific language governing permissions and
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/new_user_dialog_id">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="@dimen/grant_admin_dialog_padding"
|
android:padding="@dimen/dialog_content_padding">
|
||||||
android:paddingBottom="0dp">
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/dialog_with_icon_icon"
|
android:id="@+id/dialog_with_icon_icon"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:contentDescription=""/>
|
android:importantForAccessibility="no"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/dialog_with_icon_title"
|
android:id="@+id/dialog_with_icon_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
style="@style/DialogWithIconTitle"
|
style="@style/DialogWithIconTitle"/>
|
||||||
android:text="@string/user_grant_admin_title"
|
|
||||||
android:textDirection="locale"/>
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/dialog_with_icon_message"
|
android:id="@+id/dialog_with_icon_message"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
style="@style/TextAppearanceSmall"
|
style="@style/TextAppearanceSmall"/>
|
||||||
android:text=""
|
|
||||||
android:textDirection="locale"/>
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/custom_layout"
|
android:id="@+id/custom_layout"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center">
|
||||||
android:paddingBottom="0dp">
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button_panel"
|
android:id="@+id/button_panel"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center">
|
||||||
android:paddingBottom="0dp">
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_cancel"
|
android:id="@+id/button_cancel"
|
||||||
style="@style/DialogButtonNegative"
|
style="@style/DialogButtonNegative"
|
||||||
@@ -64,36 +67,34 @@
|
|||||||
android:buttonCornerRadius="28dp"
|
android:buttonCornerRadius="28dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_back"
|
android:id="@+id/button_back"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/DialogButtonNegative"
|
style="@style/DialogButtonNegative"
|
||||||
android:buttonCornerRadius="40dp"
|
android:buttonCornerRadius="40dp"
|
||||||
android:clickable="true"
|
android:visibility="gone"/>
|
||||||
android:focusable="true"
|
|
||||||
android:text="Back"
|
|
||||||
android:visibility="gone"
|
|
||||||
/>
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_weight="0.05"
|
android:layout_weight="0.05">
|
||||||
>
|
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_ok"
|
android:id="@+id/button_ok"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/DialogButtonPositive"
|
style="@style/DialogButtonPositive"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/user_info_title"
|
android:id="@+id/user_info_title"
|
||||||
android:layout_width="wrap_content"
|
android:gravity="center"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/EditUserDialogTitle"
|
style="@style/EditUserDialogTitle"
|
||||||
android:text="@string/user_info_settings_title"
|
android:text="@string/user_info_settings_title"
|
||||||
|
|||||||
@@ -14,10 +14,14 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/grant_admin_view"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="@dimen/grant_admin_dialog_padding">
|
android:paddingLeft="@dimen/dialog_content_padding"
|
||||||
|
android:paddingBottom="@dimen/dialog_content_padding"
|
||||||
|
android:paddingRight="@dimen/dialog_content_padding">
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
android:id="@+id/choose_admin"
|
android:id="@+id/choose_admin"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -25,13 +29,19 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/grant_admin_yes"
|
android:id="@+id/grant_admin_yes"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/radio_button_padding"
|
||||||
|
android:paddingBottom="@dimen/radio_button_padding"
|
||||||
|
android:paddingStart="@dimen/radio_button_padding_start"
|
||||||
android:text="@string/grant_admin"/>
|
android:text="@string/grant_admin"/>
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/grant_admin_no"
|
android:id="@+id/grant_admin_no"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/radio_button_padding"
|
||||||
|
android:paddingBottom="@dimen/radio_button_padding"
|
||||||
|
android:paddingStart="@dimen/radio_button_padding_start"
|
||||||
android:text="@string/not_grant_admin"/>
|
android:text="@string/not_grant_admin"/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -112,8 +112,9 @@
|
|||||||
<dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
|
<dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
|
||||||
<dimen name="broadcast_dialog_margin">16dp</dimen>
|
<dimen name="broadcast_dialog_margin">16dp</dimen>
|
||||||
|
|
||||||
<!-- Size of grant admin privileges dialog padding -->
|
<dimen name="dialog_content_padding">16dp</dimen>
|
||||||
<dimen name="grant_admin_dialog_padding">16dp</dimen>
|
<dimen name="radio_button_padding">12dp</dimen>
|
||||||
|
<dimen name="radio_button_padding_start">8dp</dimen>
|
||||||
|
|
||||||
<dimen name="dialog_button_horizontal_padding">16dp</dimen>
|
<dimen name="dialog_button_horizontal_padding">16dp</dimen>
|
||||||
<dimen name="dialog_button_vertical_padding">8dp</dimen>
|
<dimen name="dialog_button_vertical_padding">8dp</dimen>
|
||||||
|
|||||||
@@ -0,0 +1,388 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.annotation.IntDef;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.internal.util.UserIcons;
|
||||||
|
import com.android.settingslib.R;
|
||||||
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
|
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||||
|
import com.android.settingslib.drawable.CircleFramedDrawable;
|
||||||
|
import com.android.settingslib.utils.CustomDialogHelper;
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class encapsulates a Dialog for editing the user nickname and photo.
|
||||||
|
*/
|
||||||
|
public class CreateUserDialogController {
|
||||||
|
|
||||||
|
private static final String KEY_AWAITING_RESULT = "awaiting_result";
|
||||||
|
private static final String KEY_CURRENT_STATE = "current_state";
|
||||||
|
private static final String KEY_SAVED_PHOTO = "pending_photo";
|
||||||
|
private static final String KEY_SAVED_NAME = "saved_name";
|
||||||
|
private static final String KEY_IS_ADMIN = "admin_status";
|
||||||
|
private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
|
||||||
|
"key_add_user_long_message_displayed";
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({EXIT_DIALOG, INITIAL_DIALOG, GRANT_ADMIN_DIALOG,
|
||||||
|
EDIT_NAME_DIALOG, CREATE_USER_AND_CLOSE})
|
||||||
|
public @interface AddUserState {}
|
||||||
|
|
||||||
|
private static final int EXIT_DIALOG = -1;
|
||||||
|
private static final int INITIAL_DIALOG = 0;
|
||||||
|
private static final int GRANT_ADMIN_DIALOG = 1;
|
||||||
|
private static final int EDIT_NAME_DIALOG = 2;
|
||||||
|
private static final int CREATE_USER_AND_CLOSE = 3;
|
||||||
|
|
||||||
|
private @AddUserState int mCurrentState;
|
||||||
|
|
||||||
|
private CustomDialogHelper mCustomDialogHelper;
|
||||||
|
|
||||||
|
private EditUserPhotoController mEditUserPhotoController;
|
||||||
|
private Bitmap mSavedPhoto;
|
||||||
|
private String mSavedName;
|
||||||
|
private Drawable mSavedDrawable;
|
||||||
|
private Boolean mIsAdmin;
|
||||||
|
private Dialog mUserCreationDialog;
|
||||||
|
private View mGrantAdminView;
|
||||||
|
private View mEditUserInfoView;
|
||||||
|
private EditText mUserNameView;
|
||||||
|
private Activity mActivity;
|
||||||
|
private ActivityStarter mActivityStarter;
|
||||||
|
private boolean mWaitingForActivityResult;
|
||||||
|
private NewUserData mSuccessCallback;
|
||||||
|
|
||||||
|
private final String mFileAuthority;
|
||||||
|
|
||||||
|
public CreateUserDialogController(String fileAuthority) {
|
||||||
|
mFileAuthority = fileAuthority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets saved values.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
mUserCreationDialog = null;
|
||||||
|
mCustomDialogHelper = null;
|
||||||
|
mEditUserPhotoController = null;
|
||||||
|
mSavedPhoto = null;
|
||||||
|
mSavedName = null;
|
||||||
|
mSavedDrawable = null;
|
||||||
|
mIsAdmin = null;
|
||||||
|
mActivity = null;
|
||||||
|
mActivityStarter = null;
|
||||||
|
mGrantAdminView = null;
|
||||||
|
mEditUserInfoView = null;
|
||||||
|
mUserNameView = null;
|
||||||
|
mSuccessCallback = null;
|
||||||
|
mCurrentState = INITIAL_DIALOG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies that the containing activity or fragment was reinitialized.
|
||||||
|
*/
|
||||||
|
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
|
String pendingPhoto = savedInstanceState.getString(KEY_SAVED_PHOTO);
|
||||||
|
if (pendingPhoto != null) {
|
||||||
|
ThreadUtils.postOnBackgroundThread(() -> {
|
||||||
|
mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(
|
||||||
|
new File(pendingPhoto));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mCurrentState = savedInstanceState.getInt(KEY_CURRENT_STATE);
|
||||||
|
if (savedInstanceState.containsKey(KEY_IS_ADMIN)) {
|
||||||
|
mIsAdmin = savedInstanceState.getBoolean(KEY_IS_ADMIN);
|
||||||
|
}
|
||||||
|
mSavedName = savedInstanceState.getString(KEY_SAVED_NAME);
|
||||||
|
mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies that the containing activity or fragment is saving its state for later use.
|
||||||
|
*/
|
||||||
|
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||||
|
if (mUserCreationDialog != null && mEditUserPhotoController != null) {
|
||||||
|
// Bitmap cannot be stored into bundle because it may exceed parcel limit
|
||||||
|
// Store it in a temporary file instead
|
||||||
|
ThreadUtils.postOnBackgroundThread(() -> {
|
||||||
|
File file = mEditUserPhotoController.saveNewUserPhotoBitmap();
|
||||||
|
if (file != null) {
|
||||||
|
savedInstanceState.putString(KEY_SAVED_PHOTO, file.getPath());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (mIsAdmin != null) {
|
||||||
|
savedInstanceState.putBoolean(KEY_IS_ADMIN, Boolean.TRUE.equals(mIsAdmin));
|
||||||
|
}
|
||||||
|
savedInstanceState.putString(KEY_SAVED_NAME, mUserNameView.getText().toString().trim());
|
||||||
|
savedInstanceState.putInt(KEY_CURRENT_STATE, mCurrentState);
|
||||||
|
savedInstanceState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies that an activity has started.
|
||||||
|
*/
|
||||||
|
public void startingActivityForResult() {
|
||||||
|
mWaitingForActivityResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies that the result from activity has been received.
|
||||||
|
*/
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
mWaitingForActivityResult = false;
|
||||||
|
if (mEditUserPhotoController != null) {
|
||||||
|
mEditUserPhotoController.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an add user dialog with option to set the user's name and photo and choose their
|
||||||
|
* admin status.
|
||||||
|
*/
|
||||||
|
public Dialog createDialog(Activity activity,
|
||||||
|
ActivityStarter activityStarter, boolean isMultipleAdminEnabled,
|
||||||
|
NewUserData successCallback, Runnable cancelCallback) {
|
||||||
|
mActivity = activity;
|
||||||
|
mCustomDialogHelper = new CustomDialogHelper(activity);
|
||||||
|
mSuccessCallback = successCallback;
|
||||||
|
mActivityStarter = activityStarter;
|
||||||
|
addCustomViews(isMultipleAdminEnabled);
|
||||||
|
mUserCreationDialog = mCustomDialogHelper.getDialog();
|
||||||
|
updateLayout();
|
||||||
|
mUserCreationDialog.setOnDismissListener(view -> {
|
||||||
|
cancelCallback.run();
|
||||||
|
clear();
|
||||||
|
});
|
||||||
|
mUserCreationDialog.setCanceledOnTouchOutside(true);
|
||||||
|
return mUserCreationDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCustomViews(boolean isMultipleAdminEnabled) {
|
||||||
|
addGrantAdminView();
|
||||||
|
addUserInfoEditView();
|
||||||
|
mCustomDialogHelper.setPositiveButton(R.string.next, view -> {
|
||||||
|
mCurrentState++;
|
||||||
|
if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
|
||||||
|
mCurrentState++;
|
||||||
|
}
|
||||||
|
updateLayout();
|
||||||
|
});
|
||||||
|
mCustomDialogHelper.setNegativeButton(R.string.back, view -> {
|
||||||
|
mCurrentState--;
|
||||||
|
if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
|
||||||
|
mCurrentState--;
|
||||||
|
}
|
||||||
|
updateLayout();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLayout() {
|
||||||
|
switch (mCurrentState) {
|
||||||
|
case INITIAL_DIALOG:
|
||||||
|
mEditUserInfoView.setVisibility(View.GONE);
|
||||||
|
mGrantAdminView.setVisibility(View.GONE);
|
||||||
|
final SharedPreferences preferences = mActivity.getPreferences(
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
final boolean longMessageDisplayed = preferences.getBoolean(
|
||||||
|
KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false);
|
||||||
|
final int messageResId = longMessageDisplayed
|
||||||
|
? R.string.user_add_user_message_short
|
||||||
|
: R.string.user_add_user_message_long;
|
||||||
|
if (!longMessageDisplayed) {
|
||||||
|
preferences.edit().putBoolean(
|
||||||
|
KEY_ADD_USER_LONG_MESSAGE_DISPLAYED,
|
||||||
|
true).apply();
|
||||||
|
}
|
||||||
|
Drawable icon = mActivity.getDrawable(R.drawable.ic_person_add);
|
||||||
|
mCustomDialogHelper.setVisibility(mCustomDialogHelper.ICON, true)
|
||||||
|
.setVisibility(mCustomDialogHelper.TITLE, true)
|
||||||
|
.setVisibility(mCustomDialogHelper.MESSAGE, true)
|
||||||
|
.setIcon(icon)
|
||||||
|
.setButtonEnabled(true)
|
||||||
|
.setTitle(R.string.user_add_user_title)
|
||||||
|
.setMessage(messageResId)
|
||||||
|
.setNegativeButtonText(R.string.cancel)
|
||||||
|
.setPositiveButtonText(R.string.next);
|
||||||
|
break;
|
||||||
|
case GRANT_ADMIN_DIALOG:
|
||||||
|
mEditUserInfoView.setVisibility(View.GONE);
|
||||||
|
mGrantAdminView.setVisibility(View.VISIBLE);
|
||||||
|
mCustomDialogHelper
|
||||||
|
.setVisibility(mCustomDialogHelper.ICON, true)
|
||||||
|
.setVisibility(mCustomDialogHelper.TITLE, true)
|
||||||
|
.setVisibility(mCustomDialogHelper.MESSAGE, true)
|
||||||
|
.setIcon(mActivity.getDrawable(R.drawable.ic_admin_panel_settings))
|
||||||
|
.setTitle(R.string.user_grant_admin_title)
|
||||||
|
.setMessage(R.string.user_grant_admin_message)
|
||||||
|
.setNegativeButtonText(R.string.back)
|
||||||
|
.setPositiveButtonText(R.string.next);
|
||||||
|
if (mIsAdmin == null) {
|
||||||
|
mCustomDialogHelper.setButtonEnabled(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EDIT_NAME_DIALOG:
|
||||||
|
mCustomDialogHelper
|
||||||
|
.setVisibility(mCustomDialogHelper.ICON, false)
|
||||||
|
.setVisibility(mCustomDialogHelper.TITLE, false)
|
||||||
|
.setVisibility(mCustomDialogHelper.MESSAGE, false)
|
||||||
|
.setNegativeButtonText(R.string.back)
|
||||||
|
.setPositiveButtonText(R.string.done);
|
||||||
|
mEditUserInfoView.setVisibility(View.VISIBLE);
|
||||||
|
mGrantAdminView.setVisibility(View.GONE);
|
||||||
|
break;
|
||||||
|
case CREATE_USER_AND_CLOSE:
|
||||||
|
Drawable newUserIcon = mEditUserPhotoController != null
|
||||||
|
? mEditUserPhotoController.getNewUserPhotoDrawable()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
String newName = mUserNameView.getText().toString().trim();
|
||||||
|
String defaultName = mActivity.getString(R.string.user_new_user_name);
|
||||||
|
String userName = !newName.isEmpty() ? newName : defaultName;
|
||||||
|
|
||||||
|
if (mSuccessCallback != null) {
|
||||||
|
mSuccessCallback.onSuccess(userName, newUserIcon,
|
||||||
|
Boolean.TRUE.equals(mIsAdmin));
|
||||||
|
}
|
||||||
|
mCustomDialogHelper.getDialog().dismiss();
|
||||||
|
clear();
|
||||||
|
break;
|
||||||
|
case EXIT_DIALOG:
|
||||||
|
mCustomDialogHelper.getDialog().dismiss();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (mCurrentState < EXIT_DIALOG) {
|
||||||
|
mCurrentState = EXIT_DIALOG;
|
||||||
|
updateLayout();
|
||||||
|
} else {
|
||||||
|
mCurrentState = CREATE_USER_AND_CLOSE;
|
||||||
|
updateLayout();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getUserIcon(Drawable defaultUserIcon) {
|
||||||
|
if (mSavedPhoto != null) {
|
||||||
|
mSavedDrawable = CircleFramedDrawable.getInstance(mActivity, mSavedPhoto);
|
||||||
|
return mSavedDrawable;
|
||||||
|
}
|
||||||
|
return defaultUserIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUserInfoEditView() {
|
||||||
|
mEditUserInfoView = View.inflate(mActivity, R.layout.edit_user_info_dialog_content, null);
|
||||||
|
mCustomDialogHelper.addCustomView(mEditUserInfoView);
|
||||||
|
setUserName();
|
||||||
|
ImageView userPhotoView = mEditUserInfoView.findViewById(R.id.user_photo);
|
||||||
|
|
||||||
|
// if oldUserIcon param is null then we use a default gray user icon
|
||||||
|
Drawable defaultUserIcon = UserIcons.getDefaultUserIcon(
|
||||||
|
mActivity.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(defaultUserIcon);
|
||||||
|
userPhotoView.setImageDrawable(userIcon);
|
||||||
|
|
||||||
|
if (isChangePhotoRestrictedByBase(mActivity)) {
|
||||||
|
// some users can't change their photos so we need to remove the suggestive icon
|
||||||
|
mEditUserInfoView.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
RestrictedLockUtils.EnforcedAdmin adminRestriction =
|
||||||
|
getChangePhotoAdminRestriction(mActivity);
|
||||||
|
if (adminRestriction != null) {
|
||||||
|
userPhotoView.setOnClickListener(view ->
|
||||||
|
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
|
||||||
|
mActivity, adminRestriction));
|
||||||
|
} else {
|
||||||
|
mEditUserPhotoController = createEditUserPhotoController(userPhotoView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUserName() {
|
||||||
|
mUserNameView = mEditUserInfoView.findViewById(R.id.user_name);
|
||||||
|
if (mSavedName == null) {
|
||||||
|
mUserNameView.setText(R.string.user_new_user_name);
|
||||||
|
} else {
|
||||||
|
mUserNameView.setText(mSavedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGrantAdminView() {
|
||||||
|
mGrantAdminView = View.inflate(mActivity, R.layout.grant_admin_dialog_content, null);
|
||||||
|
mCustomDialogHelper.addCustomView(mGrantAdminView);
|
||||||
|
RadioGroup radioGroup = mGrantAdminView.findViewById(R.id.choose_admin);
|
||||||
|
radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
mCustomDialogHelper.setButtonEnabled(true);
|
||||||
|
mIsAdmin = checkedId == R.id.grant_admin_yes;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (Boolean.TRUE.equals(mIsAdmin)) {
|
||||||
|
RadioButton button = radioGroup.findViewById(R.id.grant_admin_yes);
|
||||||
|
button.setChecked(true);
|
||||||
|
} else if (Boolean.FALSE.equals(mIsAdmin)) {
|
||||||
|
RadioButton button = radioGroup.findViewById(R.id.grant_admin_no);
|
||||||
|
button.setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isChangePhotoRestrictedByBase(Context context) {
|
||||||
|
return RestrictedLockUtilsInternal.hasBaseUserRestriction(
|
||||||
|
context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) {
|
||||||
|
return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
|
||||||
|
context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
EditUserPhotoController createEditUserPhotoController(ImageView userPhotoView) {
|
||||||
|
return new EditUserPhotoController(mActivity, mActivityStarter, userPhotoView,
|
||||||
|
mSavedPhoto, mSavedDrawable, mFileAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return mCustomDialogHelper != null && mCustomDialogHelper.getDialog() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a callback when a new user data is filled out.
|
||||||
|
*/
|
||||||
|
public interface NewUserData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes data relevant to new user that needs to be created.
|
||||||
|
* @param userName New user name.
|
||||||
|
* @param userImage New user icon.
|
||||||
|
* @param isNewUserAdmin A boolean that indicated whether new user has admin status.
|
||||||
|
*/
|
||||||
|
void onSuccess(String userName, Drawable userImage, Boolean isNewUserAdmin);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import com.android.settingslib.R;
|
||||||
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Answers;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.android.controller.ActivityController;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class CreateUserDialogControllerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ActivityStarter mActivityStarter;
|
||||||
|
|
||||||
|
private boolean mPhotoRestrictedByBase;
|
||||||
|
private Activity mActivity;
|
||||||
|
private TestCreateUserDialogController mUnderTest;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
|
||||||
|
mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
|
||||||
|
mUnderTest = new TestCreateUserDialogController();
|
||||||
|
mPhotoRestrictedByBase = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positiveButton_grantAdminStage_noValue_OkButtonShouldBeDisabled() {
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
final AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, null,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(true);
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(false);
|
||||||
|
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positiveButton_MultipleAdminDisabled_shouldSkipGrantAdminStage() {
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
final AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, false, null,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(true);
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(true);
|
||||||
|
Button back = dialog.findViewById(R.id.button_cancel);
|
||||||
|
back.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void editUserInfoController_shouldOnlyBeVisibleOnLastStage() {
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
final AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, null,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
|
||||||
|
assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
next.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility())
|
||||||
|
.isEqualTo(View.VISIBLE);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positiveButton_MultipleAdminEnabled_shouldShowGrantAdminStage() {
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
final AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, null,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(dialog.findViewById(R.id.button_ok).isEnabled()).isEqualTo(true);
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility())
|
||||||
|
.isEqualTo(View.VISIBLE);
|
||||||
|
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
|
||||||
|
next.performClick();
|
||||||
|
assertThat(dialog.findViewById(R.id.grant_admin_view).getVisibility()).isEqualTo(View.GONE);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cancelCallback_isCalled_whenCancelled() {
|
||||||
|
NewUserData successCallback = mock(NewUserData.class);
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, successCallback,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
dialog.cancel();
|
||||||
|
verifyNoInteractions(successCallback);
|
||||||
|
verify(cancelCallback, times(1))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cancelCallback_isCalled_whenNegativeButtonClickedOnFirstStage() {
|
||||||
|
NewUserData successCallback = mock(NewUserData.class);
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, successCallback,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
Button back = dialog.findViewById(R.id.button_cancel);
|
||||||
|
back.performClick();
|
||||||
|
verifyNoInteractions(successCallback);
|
||||||
|
verify(cancelCallback, times(1))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cancelCallback_isNotCalled_whenNegativeButtonClickedOnSecondStage() {
|
||||||
|
NewUserData successCallback = mock(NewUserData.class);
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, successCallback,
|
||||||
|
cancelCallback);
|
||||||
|
dialog.show();
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
Button back = dialog.findViewById(R.id.button_cancel);
|
||||||
|
back.performClick();
|
||||||
|
verifyNoInteractions(successCallback);
|
||||||
|
verifyNoInteractions(cancelCallback);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void successCallback_isCalled_setNameAndAdminStatus() {
|
||||||
|
NewUserData successCallback = mock(NewUserData.class);
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, true, successCallback,
|
||||||
|
cancelCallback);
|
||||||
|
// No photo chosen
|
||||||
|
when(mUnderTest.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
|
||||||
|
dialog.show();
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
|
||||||
|
next.performClick();
|
||||||
|
String expectedNewName = "Test";
|
||||||
|
EditText editText = dialog.findViewById(R.id.user_name);
|
||||||
|
editText.setText(expectedNewName);
|
||||||
|
next.performClick();
|
||||||
|
verify(successCallback, times(1))
|
||||||
|
.onSuccess(expectedNewName, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void successCallback_isCalled_setName_MultipleAdminDisabled() {
|
||||||
|
NewUserData successCallback = mock(NewUserData.class);
|
||||||
|
Runnable cancelCallback = mock(Runnable.class);
|
||||||
|
|
||||||
|
AlertDialog dialog = (AlertDialog) mUnderTest.createDialog(mActivity,
|
||||||
|
mActivityStarter, false, successCallback,
|
||||||
|
cancelCallback);
|
||||||
|
// No photo chosen
|
||||||
|
when(mUnderTest.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
|
||||||
|
dialog.show();
|
||||||
|
Button next = dialog.findViewById(R.id.button_ok);
|
||||||
|
next.performClick();
|
||||||
|
String expectedNewName = "Test";
|
||||||
|
EditText editText = dialog.findViewById(R.id.user_name);
|
||||||
|
editText.setText(expectedNewName);
|
||||||
|
next.performClick();
|
||||||
|
verify(successCallback, times(1))
|
||||||
|
.onSuccess(expectedNewName, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestCreateUserDialogController extends CreateUserDialogController {
|
||||||
|
private EditUserPhotoController mPhotoController;
|
||||||
|
|
||||||
|
TestCreateUserDialogController() {
|
||||||
|
super("file_authority");
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditUserPhotoController getPhotoController() {
|
||||||
|
return mPhotoController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
EditUserPhotoController createEditUserPhotoController(ImageView userPhotoView) {
|
||||||
|
mPhotoController = mock(EditUserPhotoController.class, Answers.RETURNS_DEEP_STUBS);
|
||||||
|
return mPhotoController;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isChangePhotoRestrictedByBase(Context context) {
|
||||||
|
return mPhotoRestrictedByBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,11 +32,9 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.logging.UiEventLogger;
|
import com.android.internal.logging.UiEventLogger;
|
||||||
import com.android.settingslib.users.EditUserInfoController;
|
import com.android.settingslib.users.CreateUserDialogController;
|
||||||
import com.android.settingslib.users.GrantAdminDialogController;
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
import com.android.systemui.user.utils.MultiUserActionsEvent;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -61,20 +59,18 @@ public class CreateUserActivity extends Activity {
|
|||||||
private static final String EXTRA_IS_KEYGUARD_SHOWING = "extra_is_keyguard_showing";
|
private static final String EXTRA_IS_KEYGUARD_SHOWING = "extra_is_keyguard_showing";
|
||||||
|
|
||||||
private final UserCreator mUserCreator;
|
private final UserCreator mUserCreator;
|
||||||
private final EditUserInfoController mEditUserInfoController;
|
private CreateUserDialogController mCreateUserDialogController;
|
||||||
private final IActivityManager mActivityManager;
|
private final IActivityManager mActivityManager;
|
||||||
private final ActivityStarter mActivityStarter;
|
private final ActivityStarter mActivityStarter;
|
||||||
private final UiEventLogger mUiEventLogger;
|
private final UiEventLogger mUiEventLogger;
|
||||||
private Dialog mGrantAdminDialog;
|
|
||||||
private Dialog mSetupUserDialog;
|
private Dialog mSetupUserDialog;
|
||||||
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
|
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
|
||||||
private boolean mGrantAdminRights;
|
|
||||||
@Inject
|
@Inject
|
||||||
public CreateUserActivity(UserCreator userCreator,
|
public CreateUserActivity(UserCreator userCreator,
|
||||||
EditUserInfoController editUserInfoController, IActivityManager activityManager,
|
CreateUserDialogController createUserDialogController, IActivityManager activityManager,
|
||||||
ActivityStarter activityStarter, UiEventLogger uiEventLogger) {
|
ActivityStarter activityStarter, UiEventLogger uiEventLogger) {
|
||||||
mUserCreator = userCreator;
|
mUserCreator = userCreator;
|
||||||
mEditUserInfoController = editUserInfoController;
|
mCreateUserDialogController = createUserDialogController;
|
||||||
mActivityManager = activityManager;
|
mActivityManager = activityManager;
|
||||||
mActivityStarter = activityStarter;
|
mActivityStarter = activityStarter;
|
||||||
mUiEventLogger = uiEventLogger;
|
mUiEventLogger = uiEventLogger;
|
||||||
@@ -86,19 +82,10 @@ public class CreateUserActivity extends Activity {
|
|||||||
setShowWhenLocked(true);
|
setShowWhenLocked(true);
|
||||||
setContentView(R.layout.activity_create_new_user);
|
setContentView(R.layout.activity_create_new_user);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
mEditUserInfoController.onRestoreInstanceState(savedInstanceState);
|
mCreateUserDialogController.onRestoreInstanceState(savedInstanceState);
|
||||||
}
|
}
|
||||||
boolean isKeyguardShowing = getIntent().getBooleanExtra(EXTRA_IS_KEYGUARD_SHOWING, true);
|
|
||||||
// Display grant admin dialog only on unlocked device to admin users if multiple admins
|
|
||||||
// are allowed on this device.
|
|
||||||
if (mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
|
|
||||||
&& !isKeyguardShowing) {
|
|
||||||
mGrantAdminDialog = buildGrantAdminDialog();
|
|
||||||
mGrantAdminDialog.show();
|
|
||||||
} else {
|
|
||||||
mSetupUserDialog = createDialog();
|
mSetupUserDialog = createDialog();
|
||||||
mSetupUserDialog.show();
|
mSetupUserDialog.show();
|
||||||
}
|
|
||||||
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
||||||
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
||||||
mBackCallback);
|
mBackCallback);
|
||||||
@@ -110,7 +97,7 @@ public class CreateUserActivity extends Activity {
|
|||||||
outState.putBundle(DIALOG_STATE_KEY, mSetupUserDialog.onSaveInstanceState());
|
outState.putBundle(DIALOG_STATE_KEY, mSetupUserDialog.onSaveInstanceState());
|
||||||
}
|
}
|
||||||
|
|
||||||
mEditUserInfoController.onSaveInstanceState(outState);
|
mCreateUserDialogController.onSaveInstanceState(outState);
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,48 +112,21 @@ public class CreateUserActivity extends Activity {
|
|||||||
|
|
||||||
private Dialog createDialog() {
|
private Dialog createDialog() {
|
||||||
String defaultUserName = getString(com.android.settingslib.R.string.user_new_user_name);
|
String defaultUserName = getString(com.android.settingslib.R.string.user_new_user_name);
|
||||||
|
boolean isKeyguardShowing = getIntent().getBooleanExtra(EXTRA_IS_KEYGUARD_SHOWING, true);
|
||||||
return mEditUserInfoController.createDialog(
|
return mCreateUserDialogController.createDialog(
|
||||||
this,
|
this,
|
||||||
this::startActivity,
|
this::startActivity,
|
||||||
null,
|
(mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
|
||||||
defaultUserName,
|
&& !isKeyguardShowing),
|
||||||
getString(com.android.settingslib.R.string.user_add_user),
|
|
||||||
this::addUserNow,
|
this::addUserNow,
|
||||||
this::finish
|
this::finish
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns dialog that allows to grant user admin rights.
|
|
||||||
*/
|
|
||||||
private Dialog buildGrantAdminDialog() {
|
|
||||||
return new GrantAdminDialogController().createDialog(
|
|
||||||
this,
|
|
||||||
(grantAdminRights) -> {
|
|
||||||
mGrantAdminDialog.dismiss();
|
|
||||||
mGrantAdminRights = grantAdminRights;
|
|
||||||
if (mGrantAdminRights) {
|
|
||||||
mUiEventLogger.log(MultiUserActionsEvent
|
|
||||||
.GRANT_ADMIN_FROM_USER_SWITCHER_CREATION_DIALOG);
|
|
||||||
} else {
|
|
||||||
mUiEventLogger.log(MultiUserActionsEvent
|
|
||||||
.NOT_GRANT_ADMIN_FROM_USER_SWITCHER_CREATION_DIALOG);
|
|
||||||
}
|
|
||||||
mSetupUserDialog = createDialog();
|
|
||||||
mSetupUserDialog.show();
|
|
||||||
},
|
|
||||||
() -> {
|
|
||||||
mGrantAdminRights = false;
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
|
mCreateUserDialogController.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,9 +138,6 @@ public class CreateUserActivity extends Activity {
|
|||||||
if (mSetupUserDialog != null) {
|
if (mSetupUserDialog != null) {
|
||||||
mSetupUserDialog.dismiss();
|
mSetupUserDialog.dismiss();
|
||||||
}
|
}
|
||||||
if (mGrantAdminDialog != null) {
|
|
||||||
mGrantAdminDialog.dismiss();
|
|
||||||
}
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +147,7 @@ public class CreateUserActivity extends Activity {
|
|||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addUserNow(String userName, Drawable userIcon) {
|
private void addUserNow(String userName, Drawable userIcon, Boolean isAdmin) {
|
||||||
mSetupUserDialog.dismiss();
|
mSetupUserDialog.dismiss();
|
||||||
userName = (userName == null || userName.trim().isEmpty())
|
userName = (userName == null || userName.trim().isEmpty())
|
||||||
? getString(com.android.settingslib.R.string.user_new_user_name)
|
? getString(com.android.settingslib.R.string.user_new_user_name)
|
||||||
@@ -198,7 +155,7 @@ public class CreateUserActivity extends Activity {
|
|||||||
|
|
||||||
mUserCreator.createUser(userName, userIcon,
|
mUserCreator.createUser(userName, userIcon,
|
||||||
userInfo -> {
|
userInfo -> {
|
||||||
if (mGrantAdminRights) {
|
if (isAdmin) {
|
||||||
mUserCreator.setUserAdmin(userInfo.id);
|
mUserCreator.setUserAdmin(userInfo.id);
|
||||||
}
|
}
|
||||||
switchToUser(userInfo.id);
|
switchToUser(userInfo.id);
|
||||||
@@ -230,7 +187,7 @@ public class CreateUserActivity extends Activity {
|
|||||||
*/
|
*/
|
||||||
private void startActivity(Intent intent, int requestCode) {
|
private void startActivity(Intent intent, int requestCode) {
|
||||||
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
||||||
mEditUserInfoController.startingActivityForResult();
|
mCreateUserDialogController.startingActivityForResult();
|
||||||
startActivityForResult(intent, requestCode);
|
startActivityForResult(intent, requestCode);
|
||||||
return true;
|
return true;
|
||||||
}, /* cancel= */ null, /* afterKeyguardGone= */ true);
|
}, /* cancel= */ null, /* afterKeyguardGone= */ true);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.systemui.user;
|
|||||||
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import com.android.settingslib.users.CreateUserDialogController;
|
||||||
import com.android.settingslib.users.EditUserInfoController;
|
import com.android.settingslib.users.EditUserInfoController;
|
||||||
import com.android.systemui.user.data.repository.UserRepositoryModule;
|
import com.android.systemui.user.data.repository.UserRepositoryModule;
|
||||||
import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
|
import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
|
||||||
@@ -45,6 +46,12 @@ public abstract class UserModule {
|
|||||||
return new EditUserInfoController(FILE_PROVIDER_AUTHORITY);
|
return new EditUserInfoController(FILE_PROVIDER_AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Provides {@link CreateUserDialogController} */
|
||||||
|
@Provides
|
||||||
|
public static CreateUserDialogController provideCreateUserDialogController() {
|
||||||
|
return new CreateUserDialogController(FILE_PROVIDER_AUTHORITY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the {@link UserHandle} for the user associated with this System UI process.
|
* Provides the {@link UserHandle} for the user associated with this System UI process.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
|||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController
|
import com.android.systemui.qs.user.UserSwitchDialogController
|
||||||
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
||||||
|
import com.android.systemui.user.CreateUserActivity
|
||||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||||
import com.android.systemui.user.data.repository.UserRepository
|
import com.android.systemui.user.data.repository.UserRepository
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
import com.android.systemui.user.data.source.UserRecord
|
||||||
@@ -455,13 +456,16 @@ constructor(
|
|||||||
UserActionModel.ADD_USER -> {
|
UserActionModel.ADD_USER -> {
|
||||||
uiEventLogger.log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER)
|
uiEventLogger.log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER)
|
||||||
val currentUser = repository.getSelectedUserInfo()
|
val currentUser = repository.getSelectedUserInfo()
|
||||||
showDialog(
|
dismissDialog()
|
||||||
ShowDialogRequestModel.ShowAddUserDialog(
|
activityStarter.startActivity(
|
||||||
userHandle = currentUser.userHandle,
|
CreateUserActivity.createIntentForStart(
|
||||||
isKeyguardShowing = keyguardInteractor.isKeyguardShowing(),
|
applicationContext,
|
||||||
showEphemeralMessage = currentUser.isGuest && currentUser.isEphemeral,
|
keyguardInteractor.isKeyguardShowing()
|
||||||
dialogShower = dialogShower,
|
),
|
||||||
)
|
/* dismissShade= */ true,
|
||||||
|
/* animationController */ null,
|
||||||
|
/* showOverLockscreenWhenLocked */ true,
|
||||||
|
/* userHandle */ currentUser.getUserHandle(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
UserActionModel.ADD_SUPERVISED_USER -> {
|
UserActionModel.ADD_SUPERVISED_USER -> {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.testing.TestableLooper
|
|||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.nullable
|
import com.android.systemui.util.mockito.nullable
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
@@ -27,9 +28,7 @@ class CreateUserActivityTest : SysuiTestCase() {
|
|||||||
createDialog(
|
createDialog(
|
||||||
/* activity = */ nullable(),
|
/* activity = */ nullable(),
|
||||||
/* activityStarter = */ nullable(),
|
/* activityStarter = */ nullable(),
|
||||||
/* oldUserIcon = */ nullable(),
|
/* isMultipleAdminsEnabled = */ any(),
|
||||||
/* defaultUserName = */ nullable(),
|
|
||||||
/* title = */ nullable(),
|
|
||||||
/* successCallback = */ nullable(),
|
/* successCallback = */ nullable(),
|
||||||
/* cancelCallback = */ nullable()
|
/* cancelCallback = */ nullable()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -482,30 +482,17 @@ class UserInteractorTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun executeAction_addUser_dialogShown() =
|
fun executeAction_addUser_dismissesDialogAndStartsActivity() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
userRepository.setUserInfos(userInfos)
|
userRepository.setUserInfos(userInfos)
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
val dialogRequest = collectLastValue(underTest.dialogShowRequests)
|
|
||||||
val dialogShower: UserSwitchDialogController.DialogShower = mock()
|
|
||||||
|
|
||||||
underTest.executeAction(UserActionModel.ADD_USER, dialogShower)
|
underTest.executeAction(UserActionModel.ADD_USER)
|
||||||
verify(uiEventLogger, times(1))
|
verify(uiEventLogger, times(1))
|
||||||
.log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER)
|
.log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER)
|
||||||
assertThat(dialogRequest())
|
|
||||||
.isEqualTo(
|
|
||||||
ShowDialogRequestModel.ShowAddUserDialog(
|
|
||||||
userHandle = userInfos[0].userHandle,
|
|
||||||
isKeyguardShowing = false,
|
|
||||||
showEphemeralMessage = false,
|
|
||||||
dialogShower = dialogShower,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
underTest.onDialogShown()
|
underTest.onDialogShown()
|
||||||
assertThat(dialogRequest()).isNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user