Add avatar picker that shows a predefined set of images.
This is the first in a series of CLs to add a new avatar picker. This CL adds the basic avatar picker screen to show a set of predefined images (that can be supplied as an array of drawables). Resources are provided to adapt the screen to phones and tablets of various sizes. Future CLs will integrate the take photo and choose photo functionality from the current dialog, and will also add a set of default icons that can be chosen if no predefined images have been provided. Bug: 215134398 Test: manual check screen on various screen sizes Change-Id: I6ce95549cc17dc5a9b67d9dc43c5b7d2bda37874
This commit is contained in:
@@ -50,6 +50,7 @@ android_library {
|
||||
"SettingsLibSettingsTransition",
|
||||
"SettingsLibActivityEmbedding",
|
||||
"SettingsLibButtonPreference",
|
||||
"setupdesign",
|
||||
],
|
||||
|
||||
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
|
||||
|
||||
@@ -18,4 +18,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.settingslib">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.android.settingslib.users.AvatarPickerActivity"
|
||||
android:theme="@style/SudThemeGlifV2.DayNight"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
23
packages/SettingsLib/res/drawable/avatar_selector.xml
Normal file
23
packages/SettingsLib/res/drawable/avatar_selector.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="?android:attr/colorPrimary"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
25
packages/SettingsLib/res/drawable/ic_account_circle.xml
Normal file
25
packages/SettingsLib/res/drawable/ic_account_circle.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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">
|
||||
<path
|
||||
android:fillColor="?android:attr/colorPrimary"
|
||||
android:pathData="M5.85,17.1q1.275,-0.975 2.85,-1.538Q10.275,15 12,15q1.725,0 3.3,0.563 1.575,0.562 2.85,1.537 0.875,-1.025 1.363,-2.325Q20,13.475 20,12q0,-3.325 -2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,1.475 0.487,2.775 0.488,1.3 1.363,2.325zM12,13q-1.475,0 -2.488,-1.012Q8.5,10.975 8.5,9.5t1.012,-2.487Q10.525,6 12,6t2.488,1.013Q15.5,8.024 15.5,9.5t-1.012,2.488Q13.475,13 12,13zM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,20q1.325,0 2.5,-0.387 1.175,-0.388 2.15,-1.113 -0.975,-0.725 -2.15,-1.113Q13.325,17 12,17t-2.5,0.387q-1.175,0.388 -2.15,1.113 0.975,0.725 2.15,1.113Q10.675,20 12,20zM12,11q0.65,0 1.075,-0.425 0.425,-0.425 0.425,-1.075 0,-0.65 -0.425,-1.075Q12.65,8 12,8q-0.65,0 -1.075,0.425Q10.5,8.85 10.5,9.5q0,0.65 0.425,1.075Q11.35,11 12,11zM12,9.5zM12,18.5z"/>
|
||||
</vector>
|
||||
|
||||
24
packages/SettingsLib/res/layout/avatar_item.xml
Normal file
24
packages/SettingsLib/res/layout/avatar_item.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_height="@dimen/avatar_size_in_picker"
|
||||
android:layout_width="@dimen/avatar_size_in_picker"
|
||||
android:layout_margin="@dimen/avatar_picker_margin"
|
||||
android:layout_gravity="center"
|
||||
android:padding="@dimen/avatar_picker_padding"
|
||||
android:background="@drawable/avatar_selector"/>
|
||||
38
packages/SettingsLib/res/layout/avatar_picker.xml
Normal file
38
packages/SettingsLib/res/layout/avatar_picker.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<com.google.android.setupdesign.GlifLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/glif_layout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:icon="@drawable/ic_account_circle"
|
||||
app:sucUsePartnerResource="true"
|
||||
app:sucHeaderText="@string/avatar_picker_title">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="center_horizontal"
|
||||
style="@style/SudContentFrame">
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/avatar_grid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.setupdesign.GlifLayout>
|
||||
21
packages/SettingsLib/res/values-w1280dp-land/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w1280dp-land/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">104dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">3dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w1440dp-land/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w1440dp-land/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">128dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">4dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w1600dp-land/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w1600dp-land/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">148dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">6dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w480dp-port/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w480dp-port/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">3</integer>
|
||||
<dimen name="avatar_size_in_picker">112dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">3dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w600dp-port/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w600dp-port/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">104dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">3dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w720dp-port/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w720dp-port/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">128dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">4dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w840dp-port/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w840dp-port/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">148dp</dimen>
|
||||
<dimen name="avatar_picker_padding">8dp</dimen>
|
||||
<dimen name="avatar_picker_margin">6dp</dimen>
|
||||
</resources>
|
||||
21
packages/SettingsLib/res/values-w960dp-land/dimens.xml
Normal file
21
packages/SettingsLib/res/values-w960dp-land/dimens.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (C) 2022 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.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="avatar_picker_columns">4</integer>
|
||||
<dimen name="avatar_size_in_picker">96dp</dimen>
|
||||
<dimen name="avatar_picker_padding">6dp</dimen>
|
||||
<dimen name="avatar_picker_margin">2dp</dimen>
|
||||
</resources>
|
||||
@@ -647,4 +647,6 @@
|
||||
<item>disabled</item>
|
||||
</array>
|
||||
|
||||
<array name="avatar_images"/>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -102,4 +102,9 @@
|
||||
<dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen>
|
||||
<dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen>
|
||||
|
||||
<integer name="avatar_picker_columns">3</integer>
|
||||
<dimen name="avatar_size_in_picker">96dp</dimen>
|
||||
<dimen name="avatar_picker_padding">6dp</dimen>
|
||||
<dimen name="avatar_picker_margin">2dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1548,4 +1548,7 @@
|
||||
|
||||
<!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_no_calling">No calling.</string>
|
||||
|
||||
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
|
||||
<string name="avatar_picker_title">Choose a profile picture</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settingslib.R;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
import com.google.android.setupdesign.util.ThemeHelper;
|
||||
|
||||
/**
|
||||
* Activity to allow the user to choose a user profile picture.
|
||||
*/
|
||||
public class AvatarPickerActivity extends Activity {
|
||||
|
||||
static final String EXTRA_AVATAR_INDEX = "avatar_index";
|
||||
|
||||
private FooterButton mDoneButton;
|
||||
private AvatarAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ThemeHelper.trySetDynamicColor(this);
|
||||
setContentView(R.layout.avatar_picker);
|
||||
|
||||
GlifLayout glifLayout = findViewById(R.id.glif_layout);
|
||||
RecyclerView recyclerView = findViewById(R.id.avatar_grid);
|
||||
FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class);
|
||||
|
||||
FooterButton secondaryButton =
|
||||
new FooterButton.Builder(this)
|
||||
.setText("Cancel")
|
||||
.setListener(view -> cancel())
|
||||
.build();
|
||||
|
||||
mDoneButton =
|
||||
new FooterButton.Builder(this)
|
||||
.setText("Done")
|
||||
.setListener(view -> confirmSelection())
|
||||
.build();
|
||||
mDoneButton.setEnabled(false);
|
||||
|
||||
mixin.setSecondaryButton(secondaryButton);
|
||||
mixin.setPrimaryButton(mDoneButton);
|
||||
|
||||
mAdapter = new AvatarAdapter();
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setLayoutManager(new GridLayoutManager(this,
|
||||
getResources().getInteger(R.integer.avatar_picker_columns)));
|
||||
}
|
||||
|
||||
private void confirmSelection() {
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EXTRA_AVATAR_INDEX, mAdapter.indexFromPosition(mAdapter.mSelectedPosition));
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
private class AvatarAdapter extends RecyclerView.Adapter<AvatarViewHolder> {
|
||||
|
||||
private static final int NONE = -1;
|
||||
private static final int AVATAR_START_POSITION = 0;
|
||||
|
||||
private final TypedArray mImageDrawables;
|
||||
private int mSelectedPosition = NONE;
|
||||
|
||||
AvatarAdapter() {
|
||||
mImageDrawables = getResources().obtainTypedArray(R.array.avatar_images);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AvatarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
View itemView = layoutInflater.inflate(R.layout.avatar_item, parent, false);
|
||||
return new AvatarViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
|
||||
Drawable drawable = mImageDrawables.getDrawable(indexFromPosition(position));
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
drawable = circularDrawableFrom((BitmapDrawable) drawable);
|
||||
} else {
|
||||
throw new IllegalStateException("Avatar drawables must be bitmaps");
|
||||
}
|
||||
viewHolder.setSelected(position == mSelectedPosition);
|
||||
viewHolder.setDrawable(drawable);
|
||||
viewHolder.setClickListener(view -> {
|
||||
if (mSelectedPosition == position) {
|
||||
deselect(position);
|
||||
} else {
|
||||
select(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return AVATAR_START_POSITION + mImageDrawables.length();
|
||||
}
|
||||
|
||||
private Drawable circularDrawableFrom(BitmapDrawable drawable) {
|
||||
Bitmap bitmap = drawable.getBitmap();
|
||||
|
||||
RoundedBitmapDrawable roundedBitmapDrawable =
|
||||
RoundedBitmapDrawableFactory.create(getResources(), bitmap);
|
||||
roundedBitmapDrawable.setCircular(true);
|
||||
|
||||
return roundedBitmapDrawable;
|
||||
}
|
||||
|
||||
private int indexFromPosition(int position) {
|
||||
return position - AVATAR_START_POSITION;
|
||||
}
|
||||
|
||||
private void select(int position) {
|
||||
final int oldSelection = mSelectedPosition;
|
||||
mSelectedPosition = position;
|
||||
notifyItemChanged(position);
|
||||
if (oldSelection != NONE) {
|
||||
notifyItemChanged(oldSelection);
|
||||
} else {
|
||||
mDoneButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void deselect(int position) {
|
||||
mSelectedPosition = NONE;
|
||||
notifyItemChanged(position);
|
||||
mDoneButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AvatarViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView mImageView;
|
||||
|
||||
AvatarViewHolder(View view) {
|
||||
super(view);
|
||||
mImageView = view.findViewById(R.id.avatar_image);
|
||||
}
|
||||
|
||||
public void setDrawable(Drawable drawable) {
|
||||
mImageView.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
public void setClickListener(View.OnClickListener listener) {
|
||||
mImageView.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
mImageView.setSelected(selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
@@ -36,6 +37,8 @@ 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 java.io.File;
|
||||
@@ -139,12 +142,20 @@ public class EditUserInfoController {
|
||||
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 the suggestive icon
|
||||
if (isChangePhotoRestrictedByBase(activity)) {
|
||||
// some users can't change their photos so we need to remove the suggestive icon
|
||||
content.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE);
|
||||
} else {
|
||||
RestrictedLockUtils.EnforcedAdmin adminRestriction =
|
||||
getChangePhotoAdminRestriction(activity);
|
||||
if (adminRestriction != null) {
|
||||
userPhotoView.setOnClickListener(view ->
|
||||
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
|
||||
activity, adminRestriction));
|
||||
} else {
|
||||
mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
|
||||
userPhotoView);
|
||||
}
|
||||
}
|
||||
|
||||
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
|
||||
@@ -203,10 +214,15 @@ public class EditUserInfoController {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean canChangePhoto(Context context) {
|
||||
return (PhotoCapabilityUtils.canCropPhoto(context)
|
||||
&& PhotoCapabilityUtils.canChoosePhoto(context))
|
||||
|| PhotoCapabilityUtils.canTakePhoto(context);
|
||||
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
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.ClipData;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
@@ -29,6 +31,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.ExifInterface;
|
||||
import android.net.Uri;
|
||||
@@ -78,6 +81,7 @@ public class EditUserPhotoController {
|
||||
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 int REQUEST_CODE_PICK_AVATAR = 1004;
|
||||
// in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
|
||||
// so we need a default photo size
|
||||
private static final int DEFAULT_PHOTO_SIZE = 500;
|
||||
@@ -113,7 +117,7 @@ public class EditUserPhotoController {
|
||||
mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
|
||||
mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
|
||||
mPhotoSize = getPhotoSize(activity);
|
||||
mImageView.setOnClickListener(v -> showUpdatePhotoPopup());
|
||||
mImageView.setOnClickListener(v -> showAvatarPicker());
|
||||
mNewUserPhotoBitmap = bitmap;
|
||||
}
|
||||
|
||||
@@ -151,6 +155,18 @@ public class EditUserPhotoController {
|
||||
copyAndCropPhoto(pictureUri);
|
||||
}
|
||||
return true;
|
||||
case REQUEST_CODE_PICK_AVATAR:
|
||||
int index = data.getIntExtra(AvatarPickerActivity.EXTRA_AVATAR_INDEX, -1);
|
||||
Resources res = mImageView.getContext().getResources();
|
||||
try (TypedArray avatarImages = res.obtainTypedArray(R.array.avatar_images)) {
|
||||
if (index >= 0 && index < avatarImages.length()) {
|
||||
Drawable drawable = avatarImages.getDrawable(index);
|
||||
if (!(drawable instanceof BitmapDrawable)) {
|
||||
return false;
|
||||
}
|
||||
onPhotoProcessed(((BitmapDrawable) drawable).getBitmap());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -159,6 +175,11 @@ public class EditUserPhotoController {
|
||||
return mNewUserPhotoDrawable;
|
||||
}
|
||||
|
||||
private void showAvatarPicker() {
|
||||
Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class);
|
||||
mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR);
|
||||
}
|
||||
|
||||
private void showUpdatePhotoPopup() {
|
||||
final Context context = mImageView.getContext();
|
||||
final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class EditUserInfoControllerTest {
|
||||
@Mock
|
||||
private ActivityStarter mActivityStarter;
|
||||
|
||||
private boolean mCanChangePhoto;
|
||||
private boolean mPhotoRestrictedByBase;
|
||||
private Activity mActivity;
|
||||
private TestEditUserInfoController mController;
|
||||
|
||||
@@ -85,8 +85,8 @@ public class EditUserInfoControllerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canChangePhoto(Context context) {
|
||||
return mCanChangePhoto;
|
||||
boolean isChangePhotoRestrictedByBase(Context context) {
|
||||
return mPhotoRestrictedByBase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class EditUserInfoControllerTest {
|
||||
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
|
||||
mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
|
||||
mController = new TestEditUserInfoController();
|
||||
mCanChangePhoto = true;
|
||||
mPhotoRestrictedByBase = true;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -260,7 +260,7 @@ public class EditUserInfoControllerTest {
|
||||
|
||||
@Test
|
||||
public void createDialog_canNotChangePhoto_nullPhotoController() {
|
||||
mCanChangePhoto = false;
|
||||
mPhotoRestrictedByBase = false;
|
||||
|
||||
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
|
||||
"test", "title", null, null);
|
||||
|
||||
Reference in New Issue
Block a user