Add disabled behavior for profile button
Change icon, text and background color for the button if the button is
disabled.
Add cross user intent check for the intent that launched the activity.
Bug: 190727775
Test: manual
Change-Id: If30a5c44e211b794f794f0f6efc7ac5ee0b94649
Merged-In: If30a5c44e211b794f794f0f6efc7ac5ee0b94649
(cherry picked from commit cd3ed8363eb2f9d301082d19e7f1a3dc8d032eae)
diff --git a/res/drawable/ic_personal_mode.xml b/res/drawable/ic_personal_mode.xml
index e48a4c8..cf4562c 100644
--- a/res/drawable/ic_personal_mode.xml
+++ b/res/drawable/ic_personal_mode.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@color/picker_profile_button_text_and_icon_color">
+ android:tint="@color/picker_profile_button_content_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"
diff --git a/res/drawable/ic_work_outline.xml b/res/drawable/ic_work_outline.xml
index 9aaca27..71d0e45 100644
--- a/res/drawable/ic_work_outline.xml
+++ b/res/drawable/ic_work_outline.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@color/picker_profile_button_text_and_icon_color">
+ android:tint="@color/picker_profile_button_content_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"/>
diff --git a/res/layout/fragment_picker_tab.xml b/res/layout/fragment_picker_tab.xml
index fbe6a30..5fb1311 100644
--- a/res/layout/fragment_picker_tab.xml
+++ b/res/layout/fragment_picker_tab.xml
@@ -35,10 +35,10 @@
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:textColor="@color/picker_profile_button_content_color"
android:text="@string/picker_work_profile"
android:visibility="gone"
- app:backgroundTint="@color/picker_profile_button_background"
+ app:backgroundTint="@color/picker_profile_button_background_color"
app:borderWidth="0dp"
app:elevation="3dp"
app:icon="@drawable/ic_work_outline"
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 34c2d34..8c7eb35 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -28,7 +28,11 @@
<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>
+
+ <!-- PhotoPicker Profile Button -->
+ <color name="picker_profile_button_content_color">#A8C7FA</color>
+ <color name="picker_profile_button_background_color">#1F1F1F</color>
+ <color name="picker_profile_disabled_button_content_color">#E3E3E3</color>
+ <color name="picker_profile_disabled_button_background_color">#DADADA</color>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index bf7b280..05d43ba 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -38,7 +38,9 @@
<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>
+ <!-- PhotoPicker Profile Button -->
+ <color name="picker_profile_button_content_color">#0B57D0</color>
+ <color name="picker_profile_button_background_color">#E8F0FE</color>
+ <color name="picker_profile_disabled_button_content_color">#1F1F1F</color>
+ <color name="picker_profile_disabled_button_background_color">#DADADA</color>
</resources>
diff --git a/src/com/android/providers/media/photopicker/data/UserIdManager.java b/src/com/android/providers/media/photopicker/data/UserIdManager.java
index ebe6aa5..f2cded5 100644
--- a/src/com/android/providers/media/photopicker/data/UserIdManager.java
+++ b/src/com/android/providers/media/photopicker/data/UserIdManager.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -30,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.util.CrossProfileUtils;
import java.util.List;
@@ -99,6 +101,26 @@
boolean isManagedUserId();
/**
+ * Whether the current user is allowed to access other profile data.
+ */
+ boolean isCrossProfileAllowed();
+
+ /**
+ * Whether cross profile access is blocked by admin for the current user.
+ */
+ boolean isBlockedByAdmin();
+
+ /**
+ * Whether the work profile corresponding to the current user is turned off.
+ */
+ boolean isWorkProfileOff();
+
+ /**
+ * Set intent to check for device admin policy.
+ */
+ void setIntentAndCheckRestrictions(Intent intent);
+
+ /**
* Creates an implementation of {@link UserIdManager}.
*/
static UserIdManager create(Context context) {
@@ -125,6 +147,13 @@
@GuardedBy("mLock")
private UserId mCurrentUserProfile = null;
+ private Intent mIntent = null;
+ // Set default values to negative case, only set as false if checks pass.
+ @GuardedBy("mLock")
+ private boolean mIsBlockedByAdmin = true;
+ @GuardedBy("mLock")
+ private boolean mIsWorkProfileOff = true;
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -192,6 +221,14 @@
setCurrentUserProfileId(getPersonalUserId());
}
+ @Override
+ public void setIntentAndCheckRestrictions(Intent intent) {
+ mIntent = intent;
+ synchronized (mLock) {
+ setCrossProfileValues();
+ }
+ }
+
public boolean isCurrentUserSelected() {
synchronized (mLock) {
return mCurrentUserProfile.equals(UserId.CURRENT_USER);
@@ -217,6 +254,7 @@
private void setUserIds() {
synchronized (mLock) {
setUserIdsInternal();
+ setCrossProfileValues();
}
}
@@ -265,5 +303,50 @@
}
}
}
+
+ @Override
+ public boolean isCrossProfileAllowed() {
+ return (!isWorkProfileOff() && !isBlockedByAdmin());
+ }
+
+ @Override
+ public boolean isWorkProfileOff() {
+ synchronized (mLock) {
+ return mIsWorkProfileOff;
+ }
+ }
+
+ @Override
+ public boolean isBlockedByAdmin() {
+ synchronized (mLock) {
+ return mIsBlockedByAdmin;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void setCrossProfileValues() {
+ final PackageManager packageManager = mContext.getPackageManager();
+ // 1. Check if PICK_IMAGES intent is allowed by admin to show cross user content
+ if (mIntent == null) {
+ Log.e(TAG, "No intent specified to check if cross profile forwarding is"
+ + " allowed.");
+ return;
+ }
+ if (!CrossProfileUtils.isIntentAllowedCrossProfileAccess(mIntent, packageManager)) {
+ mIsBlockedByAdmin = true;
+ return;
+ }
+ mIsBlockedByAdmin = false;
+
+ // 2. Check if work profile is off
+ if (!isManagedUserSelected()) {
+ final UserId managedUserProfileId = getManagedUserId();
+ if (!CrossProfileUtils.isMediaProviderAvailable(managedUserProfileId, mContext)) {
+ mIsWorkProfileOff = true;
+ return;
+ }
+ }
+ mIsWorkProfileOff = false;
+ }
}
}
diff --git a/src/com/android/providers/media/photopicker/ui/TabFragment.java b/src/com/android/providers/media/photopicker/ui/TabFragment.java
index f7f6277..c8237d6 100644
--- a/src/com/android/providers/media/photopicker/ui/TabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/TabFragment.java
@@ -16,10 +16,8 @@
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;
@@ -27,17 +25,17 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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 com.google.android.material.snackbar.Snackbar;
import java.text.NumberFormat;
import java.util.Locale;
@@ -123,65 +121,83 @@
}
private void setUpProfileButton() {
- updateProfileIconAndText(mUserIdManager.isManagedUserSelected());
+ // TODO(b/190727775): Update profile button values onResume(). also re-check cross-profile
+ // restrictions.
+ updateProfileButtonContent(mUserIdManager.isManagedUserSelected());
+ updateProfileButtonColor(/* isDisabled */ !mUserIdManager.isCrossProfileAllowed());
- 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();
- }
- });
+ mProfileButton.setOnClickListener(v -> onClickProfileButton(v));
}
- private void updateProfileIconAndText(boolean isManagedUserSelected) {
- if (isManagedUserSelected) {
- mProfileButton.setIconResource(R.drawable.ic_personal_mode);
- mProfileButton.setText(R.string.picker_personal_profile);
+ private void onClickProfileButton(View v) {
+ if (!mUserIdManager.isCrossProfileAllowed()) {
+ onClickShowErrorDialog(v);
} else {
- mProfileButton.setIconResource(R.drawable.ic_work_outline);
- mProfileButton.setText(R.string.picker_work_profile);
+ onClickChangeProfile();
}
- mProfileButton.setIconTintResource(R.color.picker_profile_button_text_and_icon_color);
+ }
+
+ private void onClickShowErrorDialog(View v) {
+ if (mUserIdManager.isBlockedByAdmin()) {
+ //TODO(b/190727775): launch dialog
+ Snackbar.make(v, "Blocked by your admin", Snackbar.LENGTH_SHORT).show();
+ return;
+ }
+ if (mUserIdManager.isWorkProfileOff()) {
+ //TODO(b/190727775): launch dialog
+ Snackbar.make(v, "Turn on work apps?", Snackbar.LENGTH_SHORT).show();
+ return;
+ }
+ return;
+ }
+
+ private void onClickChangeProfile() {
+ 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();
+ }
+
+ updateProfileButtonContent(mUserIdManager.isManagedUserSelected());
+
+ mPickerViewModel.updateItems();
+ mPickerViewModel.updateCategories();
+ }
+
+ private void updateProfileButtonContent(boolean isManagedUserSelected) {
+ final int iconResId;
+ final int textResId;
+ if (isManagedUserSelected) {
+ iconResId = R.drawable.ic_personal_mode;
+ textResId = R.string.picker_personal_profile;
+ } else {
+ iconResId = R.drawable.ic_work_outline;
+ textResId = R.string.picker_work_profile;
+ }
+ mProfileButton.setIconResource(iconResId);
+ mProfileButton.setText(textResId);
+ }
+
+ private void updateProfileButtonColor(boolean isDisabled) {
+ final int textAndIconResId;
+ final int backgroundTintResId;
+ if (isDisabled) {
+ textAndIconResId = R.color.picker_profile_disabled_button_content_color;
+ backgroundTintResId = R.color.picker_profile_disabled_button_background_color;
+ } else {
+ textAndIconResId = R.color.picker_profile_button_content_color;
+ backgroundTintResId = R.color.picker_profile_button_background_color;
+ }
+ mProfileButton.setTextColor(AppCompatResources.getColorStateList(getContext(),
+ textAndIconResId));
+ mProfileButton.setIconTintResource(textAndIconResId);
+ mProfileButton.setBackgroundTintList(AppCompatResources.getColorStateList(getContext(),
+ backgroundTintResId));
}
protected int getBottomGapForRecyclerView(int bottomBarSize) {
diff --git a/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java b/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
index d262e28..728c03e 100644
--- a/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
+++ b/src/com/android/providers/media/photopicker/util/CrossProfileUtils.java
@@ -42,9 +42,10 @@
* {@link MediaStore#ACTION_PICK_IMAGES} intent, please modify the logic if we want to check
* for multiple intents.
*/
- public static boolean isPickImagesIntentAllowedCrossProfileAccess(
+ public static boolean isIntentAllowedCrossProfileAccess(Intent intent,
PackageManager packageManager) {
- final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.setComponent(null);
+ intent.setPackage(null);
for (ResolveInfo info : packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY)) {
if (info != null && info.isCrossProfileIntentForwarderActivity()) {
diff --git a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
index e92ff6c..ff57dce 100644
--- a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
+++ b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
@@ -339,6 +339,8 @@
* Parse values from {@code intent} and set corresponding fields
*/
public void parseValuesFromIntent(Intent intent) throws IllegalArgumentException {
+ mUserIdManager.setIntentAndCheckRestrictions(intent);
+
mSelectMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
final String mimeType = intent.getType();