Change work profile switch to floating button
This is an initial CL to replace the old switch button. This CL only
focusses on introducing the new floating action button:
* Remove the work profile switch
* Add extended floating action button for switching between profiles
* Hide the button in preview mode and when an item is selected
* Hide the button in Album photos view
More specifications will follow soon
Bug: 190727775
Test: manual
Change-Id: I9231a9d13bc5e237feb137ed9c43bd5e933a8e53
Merged-In: I9231a9d13bc5e237feb137ed9c43bd5e933a8e53
(cherry picked from commit 3970fee99898cd4ab20aa8a3bacedf7b833f5c84)
diff --git a/res/drawable/ic_personal_mode.xml b/res/drawable/ic_personal_mode.xml
new file mode 100644
index 0000000..e48a4c8
--- /dev/null
+++ b/res/drawable/ic_personal_mode.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2021 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="@color/picker_profile_button_text_and_icon_color">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.08,4.92C15.16,1.02 8.82,1.03 4.91,4.93C1.03,8.85 1.03,15.17 4.92,19.08C6.8,20.95 9.35,22 12,22c2.65,0 5.2,-1.05 7.08,-2.92C22.97,15.16 22.98,8.83 19.08,4.92zM6.34,17.66L6.34,17.66c0.86,-0.8 3.22,-2.16 5.67,-2.16c2.45,0 4.64,1.24 5.65,2.16C14.53,20.77 9.48,20.77 6.34,17.66zM18.93,16.03c-3.99,-3.36 -9.82,-3.36 -13.82,0c-1.77,-3.07 -1.38,-7.05 1.22,-9.69c3.13,-3.12 8.21,-3.13 11.34,-0.01C20.23,8.91 20.75,12.88 18.93,16.03zM15,8.99c0,1.66 -1.34,3 -3,3c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3C13.66,5.99 15,7.34 15,8.99z"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_work_outline.xml b/res/drawable/ic_work_outline.xml
new file mode 100644
index 0000000..9aaca27
--- /dev/null
+++ b/res/drawable/ic_work_outline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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="@color/picker_profile_button_text_and_icon_color">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4L10,4zM20,19L4,19L4,8h16v11z"/>
+</vector>
diff --git a/res/layout/activity_photo_picker.xml b/res/layout/activity_photo_picker.xml
index 8807d35..688b4ec 100644
--- a/res/layout/activity_photo_picker.xml
+++ b/res/layout/activity_photo_picker.xml
@@ -35,14 +35,6 @@
app:behavior_hideable="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
- <Switch
- android:visibility="gone"
- android:layout_gravity="end|top"
- android:text="Work Profile"
- android:id="@+id/workprofile"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
@@ -70,10 +62,8 @@
android:orientation="horizontal"/>
</androidx.appcompat.widget.Toolbar>
-
</com.google.android.material.appbar.AppBarLayout>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
-
diff --git a/res/layout/fragment_picker_tab.xml b/res/layout/fragment_picker_tab.xml
index 892e853..fbe6a30 100644
--- a/res/layout/fragment_picker_tab.xml
+++ b/res/layout/fragment_picker_tab.xml
@@ -28,6 +28,22 @@
android:drawSelectorOnTop="true"
android:overScrollMode="never"/>
+ <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
+ android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/picker_profile_button_margin_bottom"
+ android:layout_gravity="bottom|center"
+ android:textAppearance="@style/PickerProfileButton"
+ android:textColor="@color/picker_profile_button_text_and_icon_color"
+ android:text="@string/picker_work_profile"
+ android:visibility="gone"
+ app:backgroundTint="@color/picker_profile_button_background"
+ app:borderWidth="0dp"
+ app:elevation="3dp"
+ app:icon="@drawable/ic_work_outline"
+ />
+
<FrameLayout
android:id="@+id/picker_bottom_bar"
android:layout_width="match_parent"
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 4d7565a..34c2d34 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -28,5 +28,7 @@
<color name="picker_toolbar_chip_text_color">#E8EAED</color>
<color name="picker_toolbar_title_color">#FFFFFF</color>
<color name="picker_message_text_color">#9AA0A6</color>
+ <color name="picker_profile_button_text_and_icon_color">#A8C7FA</color>
+ <color name="picker_profile_button_background">#1F1F1F</color>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d3def67..bf7b280 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,4 +37,8 @@
<color name="preview_default_blue">#8AB4F8</color>
<color name="preview_default_grey">#202124</color>
<color name="preview_default_black">@android:color/black</color>
+
+ <!-- PhotoPicker Preview -->
+ <color name="picker_profile_button_text_and_icon_color">#0B57D0</color>
+ <color name="picker_profile_button_background">#E8F0FE</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c911972..8e6e12f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -65,4 +65,6 @@
<!-- PhotoPicker Preview text -->
<dimen name="preview_add_text_size">14sp</dimen>
<dimen name="preview_deselect_text_size">16sp</dimen>
+ <!-- PhotoPicker Work Profile -->
+ <dimen name="picker_profile_button_margin_bottom">32dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1b93546..92a6c30 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -106,6 +106,10 @@
<!-- The text of the albums chip on the toolbar for PhotoPicker. [CHAR LIMIT=30] -->
<string name="picker_albums">Albums</string>
+ <!-- The text of the switching work/personal profile in PhotoPicker. [CHAR LIMIT=30] -->
+ <string name="picker_work_profile">Switch to work</string>
+ <string name="picker_personal_profile">Switch to personal</string>
+
<!-- Text shown on the album item in PhotoPicker. [CHAR LIMIT=30] -->
<plurals name="picker_album_item_count">
<item quantity="one"><xliff:g id="count" example="1">^1</xliff:g> item</item>
diff --git a/res/values/styles_text.xml b/res/values/styles_text.xml
index e89a43e..7842c58 100644
--- a/res/values/styles_text.xml
+++ b/res/values/styles_text.xml
@@ -29,4 +29,8 @@
<item name="android:textSize">@dimen/picker_chip_text_size</item>
</style>
+ <style name="PickerProfileButton" parent="@android:style/TextAppearance.Material.Button">
+ <item name="android:textAllCaps">false</item>
+ </style>
+
</resources>
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
index f30d870..9043778 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
@@ -20,8 +20,6 @@
import android.annotation.IntDef;
import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -41,9 +39,6 @@
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.WindowInsetsController;
-import android.widget.CompoundButton;
-import android.widget.Switch;
-import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
@@ -51,14 +46,11 @@
import androidx.lifecycle.ViewModelProvider;
import com.android.providers.media.R;
-import com.android.providers.media.photopicker.data.UserIdManager;
import com.android.providers.media.photopicker.data.model.Category;
import com.android.providers.media.photopicker.data.model.Item;
-import com.android.providers.media.photopicker.data.model.UserId;
import com.android.providers.media.photopicker.ui.AlbumsTabFragment;
import com.android.providers.media.photopicker.ui.PhotosTabFragment;
import com.android.providers.media.photopicker.ui.PreviewFragment;
-import com.android.providers.media.photopicker.util.CrossProfileUtils;
import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
@@ -91,7 +83,6 @@
@interface TabChipType {}
private PickerViewModel mPickerViewModel;
- private UserIdManager mUserIdManager;
private ViewGroup mTabChipContainer;
private Chip mPhotosTabChip;
private Chip mAlbumsTabChip;
@@ -129,11 +120,6 @@
initBottomSheetBehavior();
restoreState(savedInstanceState);
- mUserIdManager = mPickerViewModel.getUserIdManager();
- if (mUserIdManager.isMultiUserProfiles()) {
- setUpWorkProfileToggleSwitch(findViewById(R.id.workprofile));
- }
-
// Save the fragment container layout so that we can adjust the padding based on preview or
// non-preview mode.
mFragmentContainerView = findViewById(R.id.fragment_container);
@@ -305,67 +291,6 @@
}
}
- private void setUpWorkProfileToggleSwitch(Switch profileSwitch) {
- // Enable the work profile switch button
- profileSwitch.setVisibility(View.VISIBLE);
-
- // Adjust the padding to make the profile switch button appear below toolbar
- profileSwitch.setPadding(profileSwitch.getPaddingLeft(), mToolbarHeight,
- profileSwitch.getPaddingRight(), profileSwitch.getPaddingBottom());
-
- if (mUserIdManager.isManagedUserId()) {
- profileSwitch.setChecked(true);
- }
-
- final Context context = this;
- profileSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- Toast.makeText(PhotoPickerActivity.this, "Switching to work profile",
- Toast.LENGTH_SHORT).show();
- // TODO(b/190024747): Add caching for performance before switching data to and
- // fro work profile
- mUserIdManager.setManagedAsCurrentUserProfile();
-
- } else {
- Toast.makeText(PhotoPickerActivity.this, "Switching to personal profile",
- Toast.LENGTH_SHORT).show();
- // TODO(b/190024747): Add caching for performance before switching data to and
- // fro work profile
- mUserIdManager.setPersonalAsCurrentUserProfile();
- }
-
- // Cross user checks
- if (!mUserIdManager.isCurrentUserSelected()) {
- final PackageManager packageManager = context.getPackageManager();
- // 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
- if (!CrossProfileUtils.isPickImagesIntentAllowedCrossProfileAccess(
- packageManager)) {
- Log.i(TAG, "Device admin restricts PhotoPicker to show cross profile "
- + "content for current user: " + UserId.CURRENT_USER);
- // TODO (b/190727775): Show informative error message to the user in UI.
- return;
- }
-
- // 2. Check if work profile is off
- if (mUserIdManager.isManagedUserSelected()) {
- final UserId currentUserProfileId =
- mUserIdManager.getCurrentUserProfileId();
- if (!CrossProfileUtils.isMediaProviderAvailable(currentUserProfileId,
- context)) {
- Log.i(TAG, "Work Profile is off, please turn work profile on to "
- + "access work profile content");
- // TODO (b/190727775): Show work profile turned off, please turn on.
- return;
- }
- }
- }
- mPickerViewModel.updateItems();
- }
- });
- }
-
public void setResultAndFinishSelf() {
final List<Item> selectedItemList = new ArrayList<>(
mPickerViewModel.getSelectedItems().getValue().values());
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
index 0dc92ef..1b8121a 100644
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
@@ -118,7 +118,9 @@
if (mIsDefaultCategory) {
getActivity().setTitle(/* title= */ "");
+ hideProfileButton(/* hide */ false);
} else {
+ hideProfileButton(/* hide */ true);
final String categoryName = Category.getCategoryName(getContext(), mCategoryType);
if (TextUtils.isEmpty(categoryName)) {
diff --git a/src/com/android/providers/media/photopicker/ui/TabFragment.java b/src/com/android/providers/media/photopicker/ui/TabFragment.java
index 9cd9647..f7f6277 100644
--- a/src/com/android/providers/media/photopicker/ui/TabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/TabFragment.java
@@ -16,8 +16,10 @@
package com.android.providers.media.photopicker.ui;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,8 +32,13 @@
import com.android.providers.media.R;
import com.android.providers.media.photopicker.PhotoPickerActivity;
+import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.util.CrossProfileUtils;
import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
+import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
+
import java.text.NumberFormat;
import java.util.Locale;
@@ -40,10 +47,16 @@
*/
public abstract class TabFragment extends Fragment {
+ private static final String TAG = "PhotoPickerTabFragment";
+
protected PickerViewModel mPickerViewModel;
protected ImageLoader mImageLoader;
protected AutoFitRecyclerView mRecyclerView;
+
private int mBottomBarSize;
+ private ExtendedFloatingActionButton mProfileButton;
+ private UserIdManager mUserIdManager;
+ private boolean mHideProfileButton;
@Override
@NonNull
@@ -56,10 +69,19 @@
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
+
mImageLoader = new ImageLoader(getContext());
mRecyclerView = view.findViewById(R.id.photo_list);
mRecyclerView.setHasFixedSize(true);
mPickerViewModel = new ViewModelProvider(requireActivity()).get(PickerViewModel.class);
+
+ mProfileButton = view.findViewById(R.id.profile_button);
+ mUserIdManager = mPickerViewModel.getUserIdManager();
+ if (mUserIdManager.isMultiUserProfiles()) {
+ mProfileButton.setVisibility(View.VISIBLE);
+ setUpProfileButton();
+ }
+
final boolean canSelectMultiple = mPickerViewModel.canSelectMultiple();
if (canSelectMultiple) {
final Button addButton = view.findViewById(R.id.button_add);
@@ -86,14 +108,97 @@
dimen = getBottomGapForRecyclerView(mBottomBarSize);
}
mRecyclerView.setPadding(0, 0, 0, dimen);
+
+ if (mUserIdManager.isMultiUserProfiles()) {
+ if (selectedItemList.size() > 0) {
+ mProfileButton.hide();
+ } else {
+ if (!mHideProfileButton) {
+ mProfileButton.show();
+ }
+ }
+ }
});
}
}
+ private void setUpProfileButton() {
+ updateProfileIconAndText(mUserIdManager.isManagedUserSelected());
+
+ final Context context = getContext();
+ mProfileButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ // Cross user checks
+ if (mUserIdManager.isCurrentUserSelected()) {
+ final PackageManager packageManager = context.getPackageManager();
+ // 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
+ if (!CrossProfileUtils.isPickImagesIntentAllowedCrossProfileAccess(
+ packageManager)) {
+ // TODO (b/190727775): Show informative error message to the user in UI.
+ return;
+ }
+
+ // 2. Check if work profile is off
+ if (!mUserIdManager.isManagedUserSelected()) {
+ final UserId managedUserProfileId =
+ mUserIdManager.getManagedUserId();
+ if (!CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId,
+ context)) {
+ Log.i(TAG, "Work Profile is off, please turn work profile on to "
+ + "access work profile content");
+ // TODO (b/190727775): Show work profile turned off, please turn on.
+ return;
+ }
+ }
+ }
+
+ if (mUserIdManager.isManagedUserSelected()) {
+ // TODO(b/190024747): Add caching for performance before switching data to and
+ // fro work profile
+ mUserIdManager.setPersonalAsCurrentUserProfile();
+
+ } else {
+ // TODO(b/190024747): Add caching for performance before switching data to and
+ // fro work profile
+ mUserIdManager.setManagedAsCurrentUserProfile();
+ }
+
+ updateProfileIconAndText(mUserIdManager.isManagedUserSelected());
+
+ mPickerViewModel.updateItems();
+ mPickerViewModel.updateCategories();
+ }
+ });
+ }
+
+ private void updateProfileIconAndText(boolean isManagedUserSelected) {
+ if (isManagedUserSelected) {
+ mProfileButton.setIconResource(R.drawable.ic_personal_mode);
+ mProfileButton.setText(R.string.picker_personal_profile);
+ } else {
+ mProfileButton.setIconResource(R.drawable.ic_work_outline);
+ mProfileButton.setText(R.string.picker_work_profile);
+ }
+ mProfileButton.setIconTintResource(R.color.picker_profile_button_text_and_icon_color);
+ }
+
protected int getBottomGapForRecyclerView(int bottomBarSize) {
return bottomBarSize;
}
+ protected void hideProfileButton(boolean hide) {
+ if (hide) {
+ mProfileButton.hide();
+ mHideProfileButton = true;
+ } else if (!hide && mUserIdManager.isMultiUserProfiles()
+ && mPickerViewModel.getSelectedItems().getValue().size() == 0) {
+ mProfileButton.show();
+ mHideProfileButton = false;
+ }
+ }
+
private static String generateAddButtonString(Context context, int size) {
final String sizeString = NumberFormat.getInstance(Locale.getDefault()).format(size);
final String template = context.getString(R.string.picker_add_button_multi_select);