summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Aditya <adityasngh@google.com> 2023-12-08 06:14:02 +0000
committer Aditya <adityasngh@google.com> 2024-02-06 09:44:02 +0000
commit77ee1ed2b79638391c7b5f908cb6cef8ecf6c898 (patch)
treec405fa460aa67c8589f847261f359ac0997e2a88
parent27ff8cf14381009c0e043ceca77d7edc5d46d976 (diff)
Generalising apis in `dirlist` directory using UserManagerState.
The changes include showing generalised cross profile permission exception error titles and messages, handling quiet mode for the private profile (in general for all profiles with hide on pause) and generalised accessibility content description strings. Bug: 309382744 Test: atest unit tests and manual testing Change-Id: I61a74959c6b89d976aa6bb50a71f93952f733462 Merged-In: I61a74959c6b89d976aa6bb50a71f93952f733462
-rw-r--r--res/layout-sw720dp/item_doc_list.xml2
-rw-r--r--res/layout/item_dir_grid.xml2
-rw-r--r--res/layout/item_doc_grid.xml4
-rw-r--r--res/layout/item_doc_list.xml2
-rw-r--r--res/layout/item_photo_grid.xml2
-rw-r--r--res/values/strings.xml31
-rw-r--r--src/com/android/documentsui/BaseActivity.java18
-rw-r--r--src/com/android/documentsui/UserManagerState.java56
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryAddonsAdapter.java24
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java388
-rw-r--r--src/com/android/documentsui/dirlist/DocumentHolder.java58
-rw-r--r--src/com/android/documentsui/dirlist/GridDirectoryHolder.java32
-rw-r--r--src/com/android/documentsui/dirlist/GridDocumentHolder.java35
-rw-r--r--src/com/android/documentsui/dirlist/GridPhotoHolder.java30
-rw-r--r--src/com/android/documentsui/dirlist/HeaderMessageDocumentHolder.java2
-rw-r--r--src/com/android/documentsui/dirlist/IconHelper.java54
-rw-r--r--src/com/android/documentsui/dirlist/InflateMessageDocumentHolder.java2
-rw-r--r--src/com/android/documentsui/dirlist/ListDocumentHolder.java26
-rw-r--r--src/com/android/documentsui/dirlist/Message.java196
-rw-r--r--src/com/android/documentsui/dirlist/MessageHolder.java2
-rw-r--r--src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java9
-rw-r--r--src/com/android/documentsui/dirlist/TransparentDividerDocumentHolder.java2
-rw-r--r--tests/common/com/android/documentsui/TestUserManagerState.java20
-rw-r--r--tests/common/com/android/documentsui/testing/TestProvidersAccess.java2
-rw-r--r--tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java4
-rw-r--r--tests/functional/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java67
-rw-r--r--tests/unit/com/android/documentsui/UserManagerStateTest.java141
-rw-r--r--tests/unit/com/android/documentsui/dirlist/AppsRowManagerTest.java38
-rw-r--r--tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java9
-rw-r--r--tests/unit/com/android/documentsui/dirlist/IconHelperTest.java95
-rw-r--r--tests/unit/com/android/documentsui/dirlist/InflateMessageDocumentHolderTest.java65
-rw-r--r--tests/unit/com/android/documentsui/dirlist/MessageTest.java76
32 files changed, 1151 insertions, 343 deletions
diff --git a/res/layout-sw720dp/item_doc_list.xml b/res/layout-sw720dp/item_doc_list.xml
index da9179606..4b2b3715a 100644
--- a/res/layout-sw720dp/item_doc_list.xml
+++ b/res/layout-sw720dp/item_doc_list.xml
@@ -95,7 +95,7 @@
android:orientation="horizontal">
<ImageView
- android:id="@+id/icon_briefcase"
+ android:id="@+id/icon_profile_badge"
android:layout_height="@dimen/briefcase_icon_size"
android:layout_width="@dimen/briefcase_icon_size"
android:layout_marginEnd="@dimen/briefcase_icon_margin"
diff --git a/res/layout/item_dir_grid.xml b/res/layout/item_dir_grid.xml
index 8720c37cf..423aba0c1 100644
--- a/res/layout/item_dir_grid.xml
+++ b/res/layout/item_dir_grid.xml
@@ -81,7 +81,7 @@
</FrameLayout>
<ImageView
- android:id="@+id/icon_briefcase"
+ android:id="@+id/icon_profile_badge"
android:layout_height="@dimen/briefcase_icon_size"
android:layout_width="@dimen/briefcase_icon_size"
android:layout_marginEnd="@dimen/briefcase_icon_margin"
diff --git a/res/layout/item_doc_grid.xml b/res/layout/item_doc_grid.xml
index 5e2e93842..d0b0ee305 100644
--- a/res/layout/item_doc_grid.xml
+++ b/res/layout/item_doc_grid.xml
@@ -146,7 +146,7 @@
android:paddingEnd="12dp">
<ImageView
- android:id="@+id/icon_briefcase"
+ android:id="@+id/icon_profile_badge"
android:layout_height="@dimen/briefcase_icon_size"
android:layout_width="@dimen/briefcase_icon_size"
android:layout_marginEnd="@dimen/briefcase_icon_margin"
@@ -161,7 +161,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
- android:layout_toEndOf="@+id/icon_briefcase"
+ android:layout_toEndOf="@+id/icon_profile_badge"
android:singleLine="true"
android:ellipsize="end"
android:textAlignment="viewStart"
diff --git a/res/layout/item_doc_list.xml b/res/layout/item_doc_list.xml
index 0942c4b5f..026c6efe6 100644
--- a/res/layout/item_doc_list.xml
+++ b/res/layout/item_doc_list.xml
@@ -92,7 +92,7 @@
android:layout_weight="1">
<ImageView
- android:id="@+id/icon_briefcase"
+ android:id="@+id/icon_profile_badge"
android:layout_height="@dimen/briefcase_icon_size"
android:layout_width="@dimen/briefcase_icon_size"
android:layout_marginEnd="@dimen/briefcase_icon_margin"
diff --git a/res/layout/item_photo_grid.xml b/res/layout/item_photo_grid.xml
index 5cf685004..cb2c40c25 100644
--- a/res/layout/item_photo_grid.xml
+++ b/res/layout/item_photo_grid.xml
@@ -104,7 +104,7 @@
</FrameLayout>
<FrameLayout
- android:id="@+id/icon_briefcase"
+ android:id="@+id/icon_profile_badge"
android:layout_width="@dimen/button_touch_size"
android:layout_height="@dimen/button_touch_size"
android:layout_alignParentBottom="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8509bd247..89e40ac4c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -189,6 +189,15 @@
<!-- Button text shown on a button when work profile is paused. Tapping the button will switch on the work profile [CHAR LIMIT=48] -->
<string name="quiet_mode_button">Turn on work apps</string>
+ <!-- Title of an error message. This screen is shown when the selected profile is paused. [CHAR LIMIT=72] -->
+ <string name="profile_quiet_mode_error_title">
+ <xliff:g id="profile" example="Work">%1$s</xliff:g> apps are paused
+ </string>
+ <!-- Button text shown on a button when selected profile is paused. Tapping the button will switch on the selected profile [CHAR LIMIT=48] -->
+ <string name="profile_quiet_mode_button">
+ Turn on <xliff:g id="profile" example="work">%1$s</xliff:g> apps
+ </string>
+
<!-- Error message title shown when a user's IT admin does not allow the user to select work files from a personal app. [CHAR LIMIT=72] -->
<string name="cant_select_work_files_error_title">Can\u2019t select work files</string>
<!-- Error message that's shown when the user's IT admin doesn't allow the user to select work files from a personal app. [CHAR LIMIT=200] -->
@@ -203,6 +212,16 @@
access personal files from a work app
</string>
+ <!-- Error message title shown when a user's cross profile setting does not allow the user to select one profile's files from another profile app. [CHAR LIMIT=72] -->
+ <string name="cant_select_cross_profile_files_error_title">Can\u2019t select
+ <xliff:g id="profile" example="work">%1$s</xliff:g> files
+ </string>
+ <!-- Error message that's shown when the user's cross profile setting doesn't allow the user to select one profile's files from another profile app. [CHAR LIMIT=200] -->
+ <string name="cant_select_cross_profile_files_error_message">Your IT admin doesn\u2019t allow
+ you to access <xliff:g id="profile" example="work">%1$s</xliff:g> files from a
+ <xliff:g id="profile" example="personal">%2$s</xliff:g> app
+ </string>
+
<!-- Error message title shown when the admin does not allow the user to save files from their personal profile to their work profile. [CHAR LIMIT=72] -->
<string name="cant_save_to_work_error_title">Can\u2019t save to work profile</string>
<!-- Error message shown when the user's IT admin doesn't allow the user to save files from their personal profile to their work profile. [CHAR LIMIT=200] -->
@@ -217,6 +236,16 @@
work files to your personal profile
</string>
+ <!-- Error message title shown when the user's cross profile setting does not allow the user to save files from one profile to another profile. [CHAR LIMIT=72] -->
+ <string name="cant_save_to_cross_profile_error_title">Can\u2019t save to
+ <xliff:g id="profile" example="work">%1$s</xliff:g> profile
+ </string>
+ <!-- Error message shown when the user's cross profile setting doesn't allow the user to save files from one profile to another profile. [CHAR LIMIT=200] -->
+ <string name="cant_save_to_cross_profile_error_message">Your IT admin doesn\u2019t allow you to
+ save <xliff:g id="profile" example="personal">%1$s</xliff:g> files to your
+ <xliff:g id="profile" example="work">%2$s</xliff:g> profile
+ </string>
+
<!-- Error message title. This message is shown when a user tries to do something on their work device, but that action isn't allowed by their IT admin. [CHAR LIMIT=72] -->
<string name="cross_profile_action_not_allowed_title">This action isn\u2019t allowed</string>
<!-- Error message. This message is shown when a user tries to do something on their work device, but that action isn't allowed by their IT admin. [CHAR LIMIT=200] -->
@@ -509,6 +538,8 @@
<string name="preview_file">Preview the file <xliff:g id="fileName" example="example.jpg">%1$s</xliff:g></string>
<!-- Content description text that's spoken by a screen reader. This text is for previewing a work file before opening it. -->
<string name="preview_work_file">Preview the work file <xliff:g id="fileName" example="example.jpg">%1$s</xliff:g></string>
+ <!-- Content description text that's spoken by a screen reader. This text is for previewing a private file before opening it. -->
+ <string name="preview_cross_profile_file">Preview the <xliff:g id="profile" example="work">%1$s</xliff:g> file <xliff:g id="fileName" example="example.jpg">%2$s</xliff:g></string>
<!-- Apps row title. [CHAR_LIMIT=60] -->
<string name="apps_row_title">Browse files in other apps</string>
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index d075651a7..42d2d9f9f 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -118,6 +118,12 @@ public abstract class BaseActivity
private PreferencesMonitor mPreferencesMonitor;
+ private boolean mHasProfileBecomeUnavailable = false;
+
+ public void setHasProfileBecomeUnavailable(boolean hasProfileBecomeUnavailable) {
+ mHasProfileBecomeUnavailable = hasProfileBecomeUnavailable;
+ }
+
public BaseActivity(@LayoutRes int layoutId, String tag) {
mLayoutId = layoutId;
mTag = tag;
@@ -266,6 +272,7 @@ public abstract class BaseActivity
cmdInterceptor);
ViewGroup chipGroup = findViewById(R.id.search_chip_group);
+
mUserIdManager = DocumentsApplication.getUserIdManager(this);
mUserManagerState = DocumentsApplication.getUserManagerState(this);
mSearchManager = new SearchViewManager(searchListener, queryInterceptor,
@@ -317,7 +324,12 @@ public abstract class BaseActivity
// The activity will clear search on root picked. If we don't clear the search,
// user may see the search result screen show up briefly and then get cleared.
mSearchManager.cancelSearch();
- mInjector.actions.loadCrossProfileRoot(getCurrentRoot(), userId);
+ // When a profile with user property SHOW_IN_QUIET_MODE_HIDDEN is currently
+ // selected, and it becomes unavailable, we reset the roots to recents.
+ // We do not reset it to recents when pick activity is due to ACTION_CREATE_DOCUMENT
+ mInjector.actions.loadCrossProfileRoot(
+ (mHasProfileBecomeUnavailable && mState.action != State.ACTION_CREATE)
+ ? getRecentsRoot() : getCurrentRoot(), userId);
}
});
@@ -868,6 +880,10 @@ public abstract class BaseActivity
}
}
+ public RootInfo getRecentsRoot() {
+ return mProviders.generateRecentsRoot(getSelectedUser());
+ }
+
@Override
public DocumentInfo getCurrentDirectory() {
return mState.stack.peek();
diff --git a/src/com/android/documentsui/UserManagerState.java b/src/com/android/documentsui/UserManagerState.java
index 50858f389..8e880600a 100644
--- a/src/com/android/documentsui/UserManagerState.java
+++ b/src/com/android/documentsui/UserManagerState.java
@@ -86,6 +86,16 @@ public interface UserManagerState {
Map<UserId, Boolean> getCanForwardToProfileIdMap(Intent intent);
/**
+ * Updates the state of the list of userIds and all the associated maps according the intent
+ * received in broadcast
+ *
+ * @param userId {@link UserId} for the profile for which the availability status changed
+ * @param action {@link Intent}.ACTION_PROFILE_UNAVAILABLE or
+ * {@link Intent}.ACTION_PROFILE_AVAILABLE
+ */
+ void onProfileActionStatusChange(String action, UserId userId);
+
+ /**
* Creates an implementation of {@link UserManagerState}.
*/
// TODO: b/314746383 Make this class a singleton
@@ -172,6 +182,7 @@ public interface UserManagerState {
public List<UserId> getUserIds() {
synchronized (mUserIds) {
if (mUserIds.isEmpty()) {
+ Log.d("profileAction", "user ids empty");
mUserIds.addAll(getUserIdsInternal());
}
return mUserIds;
@@ -208,6 +219,47 @@ public interface UserManagerState {
}
}
+ @Override
+ @SuppressLint("NewApi")
+ public void onProfileActionStatusChange(String action, UserId userId) {
+ UserProperties userProperties = mUserManager.getUserProperties(
+ UserHandle.of(userId.getIdentifier()));
+ if (userProperties.getShowInQuietMode() != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
+ return;
+ }
+ if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)) {
+ synchronized (mUserIds) {
+ mUserIds.remove(userId);
+ }
+ synchronized (mUserIdToLabelMap) {
+ mUserIdToLabelMap.remove(userId);
+ }
+ synchronized (mUserIdToBadgeMap) {
+ mUserIdToBadgeMap.remove(userId);
+ }
+ synchronized (mCanFrowardToProfileIdMap) {
+ mCanFrowardToProfileIdMap.remove(userId);
+ }
+ } else if (Intent.ACTION_PROFILE_AVAILABLE.equals(action)) {
+ synchronized (mUserIds) {
+ if (!mUserIds.contains(userId)) {
+ mUserIds.add(userId);
+ }
+ }
+ synchronized (mUserIdToLabelMap) {
+ mUserIdToLabelMap.put(userId, getProfileLabel(userId));
+ }
+ synchronized (mUserIdToBadgeMap) {
+ mUserIdToBadgeMap.put(userId, getProfileBadge(userId));
+ }
+ synchronized (mCanFrowardToProfileIdMap) {
+ mCanFrowardToProfileIdMap.put(userId, true);
+ }
+ } else {
+ Log.e(TAG, "Unexpected action received: " + action);
+ }
+ }
+
private List<UserId> getUserIdsInternal() {
final List<UserId> result = new ArrayList<>();
@@ -488,11 +540,11 @@ public interface UserManagerState {
* 2. current user does not delegate check to the parent and the target user is the
* parent profile
*/
- UserId needToCheck;
+ UserId needToCheck = null;
if (parentOrDelegatedFromParent.contains(mCurrentUser)
&& !noDelegation.isEmpty()) {
needToCheck = noDelegation.get(0);
- } else {
+ } else if (mCurrentUser.getIdentifier() != ActivityManager.getCurrentUser()) {
final UserHandle parentProfile = mUserManager.getProfileParent(
UserHandle.of(mCurrentUser.getIdentifier()));
needToCheck = UserId.of(parentProfile);
diff --git a/src/com/android/documentsui/dirlist/DirectoryAddonsAdapter.java b/src/com/android/documentsui/dirlist/DirectoryAddonsAdapter.java
index 7bccbac4e..335126e59 100644
--- a/src/com/android/documentsui/dirlist/DirectoryAddonsAdapter.java
+++ b/src/com/android/documentsui/dirlist/DirectoryAddonsAdapter.java
@@ -16,8 +16,10 @@
package com.android.documentsui.dirlist;
+import android.os.UserManager;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
@@ -25,10 +27,13 @@ import com.android.documentsui.Model;
import com.android.documentsui.Model.Update;
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.State;
+import com.android.documentsui.base.UserId;
import com.android.documentsui.dirlist.Message.HeaderMessage;
import com.android.documentsui.dirlist.Message.InflateMessage;
+import com.android.documentsui.util.FeatureFlagUtils;
import java.util.List;
+import java.util.Map;
/**
* Adapter wrapper that embellishes the directory list by inserting Holder views inbetween
@@ -49,12 +54,23 @@ final class DirectoryAddonsAdapter extends DocumentsAdapter {
private final Message mInflateMessage;
DirectoryAddonsAdapter(Environment environment, DocumentsAdapter delegate) {
+ this(environment, delegate, null, null, null, null);
+ }
+
+ DirectoryAddonsAdapter(Environment environment, DocumentsAdapter delegate,
+ @Nullable UserId sourceUserId, @Nullable UserId selectedUserId,
+ @Nullable Map<UserId, String> userIdLabelMap, UserManager userManager) {
mEnv = environment;
mDelegate = delegate;
// TODO: We should not instantiate the messages here, but rather instantiate them
// when we get an update event.
mHeaderMessage = new HeaderMessage(environment, this::onDismissHeaderMessage);
- mInflateMessage = new InflateMessage(environment, this::onDismissHeaderMessage);
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ mInflateMessage = new InflateMessage(environment, this::onDismissHeaderMessage,
+ sourceUserId, selectedUserId, userIdLabelMap, userManager);
+ } else {
+ mInflateMessage = new InflateMessage(environment, this::onDismissHeaderMessage);
+ }
// Relay events published by our delegate to our listeners (presumably RecyclerView)
// with adjusted positions.
@@ -295,13 +311,13 @@ final class DirectoryAddonsAdapter extends DocumentsAdapter {
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
- assert(itemCount == 1);
+ assert (itemCount == 1);
notifyItemRangeChanged(toViewPosition(positionStart), itemCount, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
- assert(itemCount == 1);
+ assert (itemCount == 1);
if (positionStart < mBreakPosition) {
mBreakPosition++;
}
@@ -310,7 +326,7 @@ final class DirectoryAddonsAdapter extends DocumentsAdapter {
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
- assert(itemCount == 1);
+ assert (itemCount == 1);
if (positionStart < mBreakPosition) {
mBreakPosition--;
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index a5306bd97..77047ad7a 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -28,6 +28,7 @@ import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserProperties;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -35,6 +36,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.util.Log;
@@ -89,6 +91,7 @@ import com.android.documentsui.ProfileTabsController;
import com.android.documentsui.R;
import com.android.documentsui.ThumbnailCache;
import com.android.documentsui.TimeoutTask;
+import com.android.documentsui.UserManagerState;
import com.android.documentsui.base.DocumentFilters;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
@@ -110,8 +113,10 @@ import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortModel;
-
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.documentsui.util.VersionUtils;
+import com.android.modules.utils.build.SdkLevel;
+
import com.google.common.base.Objects;
import java.io.IOException;
@@ -132,7 +137,8 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
REQUEST_COPY_DESTINATION
})
@Retention(RetentionPolicy.SOURCE)
- public @interface RequestCode {}
+ public @interface RequestCode {
+ }
public static final int REQUEST_COPY_DESTINATION = 1;
@@ -236,33 +242,92 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (isManagedProfileAction(action)) {
- UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
- UserId userId = UserId.of(userHandle);
+ if (SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ profileStatusReceiverPostV(intent, action);
+ } else {
+ profileStatusReceiverPreV(intent, action);
+ }
+
+ }
+
+ private void profileStatusReceiverPostV(Intent intent, String action) {
+ if (!SdkLevel.isAtLeastV()) return;
+ if (!isProfileStatusAction(action)) return;
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ UserId userId = UserId.of(userHandle);
+ UserManager userManager = mActivity.getSystemService(UserManager.class);
+ if (userManager == null) {
+ Log.e(TAG, "cannot obtain user manager");
+ return;
+ }
+ UserProperties userProperties = userManager.getUserProperties(userHandle);
+ if (userProperties.getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_PAUSED) {
if (Objects.equal(mActivity.getSelectedUser(), userId)) {
// We only need to refresh the layout when the selected user is equal to the
// received profile user.
- if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
- // If the managed profile is turned off, we need to refresh the directory
- // to update the UI to show an appropriate error message.
- if (mProviderTestRunnable != null) {
- mHandler.removeCallbacks(mProviderTestRunnable);
- mProviderTestRunnable = null;
- }
- onRefresh();
- return;
- }
-
- // When the managed profile becomes available, the provider may not be available
- // immediately, we need to check if it is ready before we reload the content.
- if (Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
- checkUriAndScheduleCheckIfNeeded(userId);
- }
+ onPausedProfileStatusChange(action, userId);
}
+ return;
+ }
+ if (userProperties.getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
+ onHiddenProfileStatusChange(action, userId);
+ }
+ }
+
+ private void profileStatusReceiverPreV(Intent intent, String action) {
+ if (!isManagedProfileAction(action)) return;
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ UserId userId = UserId.of(userHandle);
+ if (Objects.equal(mActivity.getSelectedUser(), userId)) {
+ // We only need to refresh the layout when the selected user is equal to the
+ // received profile user.
+ onPausedProfileStatusChange(action, userId);
}
}
};
+ private void onPausedProfileStatusChange(String action, UserId userId) {
+ if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
+ || (SdkLevel.isAtLeastV() && Intent.ACTION_PROFILE_UNAVAILABLE.equals(action))) {
+ // If the managed/paused profile is turned off, we need to refresh the directory
+ // to update the UI to show an appropriate error message.
+ if (mProviderTestRunnable != null) {
+ mHandler.removeCallbacks(mProviderTestRunnable);
+ mProviderTestRunnable = null;
+ }
+ onRefresh();
+ return;
+ }
+
+ // When the managed/paused profile becomes available, the provider may not be available
+ // immediately, we need to check if it is ready before we reload the content.
+ if (Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
+ || (SdkLevel.isAtLeastV() && Intent.ACTION_PROFILE_AVAILABLE.equals(action))) {
+ checkUriAndScheduleCheckIfNeeded(userId);
+ }
+ }
+
+ private void onHiddenProfileStatusChange(String action, UserId userId) {
+ UserManagerState userManagerState = DocumentsApplication.getUserManagerState(mActivity);
+ userManagerState.onProfileActionStatusChange(action, userId);
+ if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)) {
+ mActivity.setHasProfileBecomeUnavailable(true);
+ if (mProviderTestRunnable != null) {
+ mHandler.removeCallbacks(mProviderTestRunnable);
+ mProviderTestRunnable = null;
+ }
+ while (mState.stack.size() > 0) {
+ mState.stack.pop();
+ }
+ mActivity.updateNavigator();
+ mActivity.setHasProfileBecomeUnavailable(false);
+ } else {
+ checkUriAndScheduleCheckIfNeeded(userId);
+ }
+ }
+
private final BroadcastReceiver mSdCardBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -288,7 +353,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
mHandler.removeCallbacks(mProviderTestRunnable);
mProviderTestRunnable = null;
}
- mHandler.post(() -> onRefresh());
+ mHandler.post(this::onRefresh);
} else {
checkUriWithDelay(/* numOfRetries= */1, uri, userId);
}
@@ -326,8 +391,8 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
private boolean isProviderAvailable(Uri uri, UserId userId) {
try (ContentProviderClient userClient =
- DocumentsApplication.acquireUnstableProviderOrThrow(
- userId.getContentResolver(mActivity), uri.getAuthority())) {
+ DocumentsApplication.acquireUnstableProviderOrThrow(
+ userId.getContentResolver(mActivity), uri.getAuthority())) {
Cursor testCursor = userClient.query(uri, /* projection= */ null,
/* queryArgs= */null, /* cancellationSignal= */ null);
if (testCursor != null) {
@@ -339,6 +404,12 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
return false;
}
+ private boolean isProfileStatusAction(String action) {
+ if (!SdkLevel.isAtLeastV()) return isManagedProfileAction(action);
+ return Intent.ACTION_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_PROFILE_UNAVAILABLE.equals(action);
+ }
+
private static boolean isManagedProfileAction(String action) {
return Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
|| Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action);
@@ -447,10 +518,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
mIconHelper = new IconHelper(mActivity, MODE_GRID, mState.supportsCrossProfile());
- mAdapter = new DirectoryAddonsAdapter(
- mAdapterEnv,
- new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper, mInjector.fileTypeLookup)
- );
+ mAdapter = getModelBackedDocumentsAdapter();
mRecView.setAdapter(mAdapter);
@@ -486,14 +554,14 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
DragStartListener dragStartListener = mInjector.config.dragAndDropEnabled()
? DragStartListener.create(
- mIconHelper,
- mModel,
- mSelectionMgr,
- mSelectionMetadata,
- mState,
- this::getModelId,
- mRecView::findChildViewUnder,
- DocumentsApplication.getDragAndDropManager(mActivity))
+ mIconHelper,
+ mModel,
+ mSelectionMgr,
+ mSelectionMetadata,
+ mState,
+ this::getModelId,
+ mRecView::findChildViewUnder,
+ DocumentsApplication.getDragAndDropManager(mActivity))
: DragStartListener.STUB;
{
@@ -505,16 +573,16 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
new DocsStableIdProvider(mAdapter),
mDetailsLookup,
StorageStrategy.createStringStorage())
- .withBandOverlay(R.drawable.band_select_overlay)
- .withFocusDelegate(mFocusManager)
- .withOnDragInitiatedListener(dragStartListener::onDragEvent)
- .withOnContextClickListener(this::onContextMenuClick)
- .withOnItemActivatedListener(this::onItemActivated)
- .withOperationMonitor(mContentLock.getMonitor())
- .withSelectionPredicate(selectionPredicate)
- .withGestureTooltypes(MotionEvent.TOOL_TYPE_FINGER,
- MotionEvent.TOOL_TYPE_STYLUS)
- .build();
+ .withBandOverlay(R.drawable.band_select_overlay)
+ .withFocusDelegate(mFocusManager)
+ .withOnDragInitiatedListener(dragStartListener::onDragEvent)
+ .withOnContextClickListener(this::onContextMenuClick)
+ .withOnItemActivatedListener(this::onItemActivated)
+ .withOperationMonitor(mContentLock.getMonitor())
+ .withSelectionPredicate(selectionPredicate)
+ .withGestureTooltypes(MotionEvent.TOOL_TYPE_FINGER,
+ MotionEvent.TOOL_TYPE_STYLUS)
+ .build();
mInjector.updateSharedSelectionTracker(localTracker);
}
@@ -573,6 +641,10 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ if (SdkLevel.isAtLeastV()) {
+ filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
+ }
// DocumentsApplication will resend the broadcast locally after roots are updated.
// Register to a local broadcast manager to avoid this fragment from updating before
// roots are updated.
@@ -581,6 +653,21 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
getContext().registerReceiver(mSdCardBroadcastReceiver, getSdCardStateChangeFilter());
}
+ private DocumentsAdapter getModelBackedDocumentsAdapter() {
+ return FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? new DirectoryAddonsAdapter(
+ mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper,
+ mInjector.fileTypeLookup),
+ UserId.CURRENT_USER,
+ mActivity.getSelectedUser(),
+ DocumentsApplication.getUserManagerState(getContext()).getUserIdToLabelMap(),
+ getContext().getSystemService(UserManager.class))
+ : new DirectoryAddonsAdapter(
+ mAdapterEnv,
+ new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper,
+ mInjector.fileTypeLookup));
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -737,7 +824,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
/**
* Updates the layout after the view mode switches.
*
- * @param mode The new view mode.
+ * @param scale The new view mode.
*/
private void scaleLayout(float scale) {
assert DEBUG;
@@ -833,127 +920,99 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
MutableSelection<String> selection = new MutableSelection<>();
mSelectionMgr.copySelection(selection);
- switch (item.getItemId()) {
- case R.id.action_menu_select:
- case R.id.dir_menu_open:
- openDocuments(selection);
- mActionModeController.finishActionMode();
- return true;
-
- case R.id.action_menu_open_with:
- case R.id.dir_menu_open_with:
- showChooserForDoc(selection);
- return true;
-
- case R.id.dir_menu_open_in_new_window:
- mActions.openSelectedInNewWindow();
- return true;
-
- case R.id.action_menu_share:
- case R.id.dir_menu_share:
- mActions.shareSelectedDocuments();
- return true;
-
- case R.id.action_menu_delete:
- case R.id.dir_menu_delete:
- // deleteDocuments will end action mode if the documents are deleted.
- // It won't end action mode if user cancels the delete.
- mActions.showDeleteDialog();
- return true;
-
- case R.id.action_menu_copy_to:
- transferDocuments(selection, null, FileOperationService.OPERATION_COPY);
- // TODO: Only finish selection mode if copy-to is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
- return true;
-
- case R.id.action_menu_compress:
- transferDocuments(selection, mState.stack,
- FileOperationService.OPERATION_COMPRESS);
- // TODO: Only finish selection mode if compress is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
- return true;
+ final int id = item.getItemId();
+ if (id == R.id.action_menu_select || id == R.id.dir_menu_open) {
+ openDocuments(selection);
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_open_with || id == R.id.dir_menu_open_with) {
+ showChooserForDoc(selection);
+ return true;
+ } else if (id == R.id.dir_menu_open_in_new_window) {
+ mActions.openSelectedInNewWindow();
+ return true;
+ } else if (id == R.id.action_menu_share || id == R.id.dir_menu_share) {
+ mActions.shareSelectedDocuments();
+ return true;
+ } else if (id == R.id.action_menu_delete || id == R.id.dir_menu_delete) {
+ // deleteDocuments will end action mode if the documents are deleted.
+ // It won't end action mode if user cancels the delete.
+ mActions.showDeleteDialog();
+ return true;
+ } else if (id == R.id.action_menu_copy_to) {
+ transferDocuments(selection, null, FileOperationService.OPERATION_COPY);
+ // TODO: Only finish selection mode if copy-to is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_compress) {
+ transferDocuments(selection, mState.stack,
+ FileOperationService.OPERATION_COMPRESS);
+ // TODO: Only finish selection mode if compress is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
// TODO: Implement extract (to the current directory).
- case R.id.action_menu_extract_to:
- transferDocuments(selection, null, FileOperationService.OPERATION_EXTRACT);
- // TODO: Only finish selection mode if compress-to is not canceled.
- // Need to plum down into handling the way we do with deleteDocuments.
- mActionModeController.finishActionMode();
- return true;
-
- case R.id.action_menu_move_to:
- if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
- mInjector.dialogs.showOperationUnsupported();
- return true;
- }
- // Exit selection mode first, so we avoid deselecting deleted documents.
- mActionModeController.finishActionMode();
- transferDocuments(selection, null, FileOperationService.OPERATION_MOVE);
- return true;
-
- case R.id.action_menu_inspect:
- case R.id.dir_menu_inspect:
- mActionModeController.finishActionMode();
- assert selection.size() <= 1;
- DocumentInfo doc = selection.isEmpty()
- ? mActivity.getCurrentDirectory()
- : mModel.getDocuments(selection).get(0);
-
- mActions.showInspector(doc);
- return true;
-
- case R.id.dir_menu_cut_to_clipboard:
- mActions.cutToClipboard();
- return true;
-
- case R.id.dir_menu_copy_to_clipboard:
- mActions.copyToClipboard();
- return true;
-
- case R.id.dir_menu_paste_from_clipboard:
- pasteFromClipboard();
- return true;
-
- case R.id.dir_menu_paste_into_folder:
- pasteIntoFolder();
- return true;
-
- case R.id.action_menu_select_all:
- case R.id.dir_menu_select_all:
- mActions.selectAllFiles();
- return true;
-
- case R.id.action_menu_deselect_all:
- case R.id.dir_menu_deselect_all:
- mActions.deselectAllFiles();
- return true;
-
- case R.id.action_menu_rename:
- case R.id.dir_menu_rename:
- renameDocuments(selection);
- return true;
-
- case R.id.dir_menu_create_dir:
- mActions.showCreateDirectoryDialog();
- return true;
-
- case R.id.dir_menu_view_in_owner:
- mActions.viewInOwner();
- return true;
-
- case R.id.action_menu_sort:
- mActions.showSortDialog();
+ } else if (id == R.id.action_menu_extract_to) {
+ transferDocuments(selection, null, FileOperationService.OPERATION_EXTRACT);
+ // TODO: Only finish selection mode if compress-to is not canceled.
+ // Need to plum down into handling the way we do with deleteDocuments.
+ mActionModeController.finishActionMode();
+ return true;
+ } else if (id == R.id.action_menu_move_to) {
+ if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
+ mInjector.dialogs.showOperationUnsupported();
return true;
-
- default:
- if (DEBUG) {
- Log.d(TAG, "Unhandled menu item selected: " + item);
- }
- return false;
+ }
+ // Exit selection mode first, so we avoid deselecting deleted documents.
+ mActionModeController.finishActionMode();
+ transferDocuments(selection, null, FileOperationService.OPERATION_MOVE);
+ return true;
+ } else if (id == R.id.action_menu_inspect || id == R.id.dir_menu_inspect) {
+ mActionModeController.finishActionMode();
+ assert selection.size() <= 1;
+ DocumentInfo doc = selection.isEmpty()
+ ? mActivity.getCurrentDirectory()
+ : mModel.getDocuments(selection).get(0);
+
+ mActions.showInspector(doc);
+ return true;
+ } else if (id == R.id.dir_menu_cut_to_clipboard) {
+ mActions.cutToClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_copy_to_clipboard) {
+ mActions.copyToClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_paste_from_clipboard) {
+ pasteFromClipboard();
+ return true;
+ } else if (id == R.id.dir_menu_paste_into_folder) {
+ pasteIntoFolder();
+ return true;
+ } else if (id == R.id.action_menu_select_all || id == R.id.dir_menu_select_all) {
+ mActions.selectAllFiles();
+ return true;
+ } else if (id == R.id.action_menu_deselect_all || id == R.id.dir_menu_deselect_all) {
+ mActions.deselectAllFiles();
+ return true;
+ } else if (id == R.id.action_menu_rename || id == R.id.dir_menu_rename) {
+ renameDocuments(selection);
+ return true;
+ } else if (id == R.id.dir_menu_create_dir) {
+ mActions.showCreateDirectoryDialog();
+ return true;
+ } else if (id == R.id.dir_menu_view_in_owner) {
+ mActions.viewInOwner();
+ return true;
+ } else if (id == R.id.action_menu_sort) {
+ mActions.showSortDialog();
+ return true;
}
+ if (DEBUG) {
+ Log.d(TAG, "Unhandled menu item selected: " + item);
+ }
+ return false;
}
private boolean onAccessibilityClick(View child) {
@@ -1358,7 +1417,6 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
mRefreshLayout.setRefreshing(false);
if (rootDoc != null && mActivity.getCurrentDirectory() == null) {
// Make sure the stack does not change during task was running.
- Log.d(TAG, "Root doc is retrieved. Pushing to the stack");
mState.stack.push(rootDoc);
mActivity.updateNavigator();
mActions.loadDocumentsForCurrentStack();
diff --git a/src/com/android/documentsui/dirlist/DocumentHolder.java b/src/com/android/documentsui/dirlist/DocumentHolder.java
index 5e38b4882..d2fa0fde2 100644
--- a/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -36,9 +36,13 @@ import androidx.annotation.RequiresApi;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
+import com.android.documentsui.UserManagerState;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
+import com.android.documentsui.base.UserId;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
import java.util.function.Function;
@@ -63,6 +67,7 @@ public abstract class DocumentHolder
private KeyboardEventListener<DocumentItemDetails> mKeyEventListener;
private final DocumentItemDetails mDetails;
+ private UserManagerState mUserManagerState = null;
public DocumentHolder(Context context, ViewGroup parent, int layout) {
this(context, inflateLayout(context, parent, layout));
@@ -75,13 +80,13 @@ public abstract class DocumentHolder
mContext = context;
mDetails = new DocumentItemDetails(this);
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ mUserManagerState = DocumentsApplication.getUserManagerState(mContext);
+ }
}
/**
* Binds the view to the given item data.
- * @param cursor
- * @param modelId
- * @param state
*/
public abstract void bind(Cursor cursor, String modelId);
@@ -95,10 +100,10 @@ public abstract class DocumentHolder
* TODO: Use the DirectoryItemAnimator instead of manually controlling animation using a boolean
* flag.
*
- * @param selected
* @param animate Whether or not to animate the change. Only selection changes initiated by the
- * selection manager should be animated. See
- * {@link ModelBackedDocumentsAdapter#onBindViewHolder(DocumentHolder, int, java.util.List)}
+ * selection manager should be animated. See
+ * {@link ModelBackedDocumentsAdapter#onBindViewHolder(DocumentHolder, int,
+ * java.util.List)}
*/
public void setSelected(boolean selected, boolean animate) {
itemView.setActivated(selected);
@@ -113,13 +118,31 @@ public abstract class DocumentHolder
mAction = action;
}
- public void bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback) {}
+ /**
+ * @param show boolean denoting whether the current profile is non-personal
+ * @param clickCallback call back function
+ */
+ public void bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback) {
+ }
- public void bindBriefcaseIcon(boolean show) {}
+ /**
+ * @param show boolean denoting whether the current profile is managed
+ */
+ public void bindBriefcaseIcon(boolean show) {
+ }
+
+ /**
+ * Binds profile badge icon to the documents thumbnail
+ *
+ * @param show boolean denoting whether the current profile is non-personal/parent
+ * @param userIdIdentifier user id of the profile the document belongs to
+ */
+ public void bindProfileIcon(boolean show, int userIdIdentifier) {
+ }
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- assert(mKeyEventListener != null);
+ assert (mKeyEventListener != null);
DocumentItemDetails details = getItemDetails();
return (details == null)
? false
@@ -135,7 +158,7 @@ public abstract class DocumentHolder
* <p>Ideally we'd not involve DocumentHolder in propagation of events like this.
*/
public void addKeyEventListener(KeyboardEventListener<DocumentItemDetails> listener) {
- assert(mKeyEventListener == null);
+ assert (mKeyEventListener == null);
mKeyEventListener = listener;
}
@@ -179,12 +202,21 @@ public abstract class DocumentHolder
return view.animate().setDuration(Shared.CHECK_ANIMATION_DURATION).alpha(alpha);
}
- protected String getPreviewIconContentDescription(boolean isWorkProfile, String fileName) {
+ protected String getPreviewIconContentDescription(boolean isNonPersonalProfile,
+ String fileName, UserId userId) {
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ String profileLabel = mUserManagerState.getUserIdToLabelMap().get(userId);
+ return isNonPersonalProfile
+ ? itemView.getResources().getString(R.string.preview_cross_profile_file,
+ profileLabel, fileName)
+ : itemView.getResources().getString(R.string.preview_file, fileName);
+ }
if (SdkLevel.isAtLeastT()) {
- return getUpdatablePreviewIconContentDescription(isWorkProfile, fileName);
+ return getUpdatablePreviewIconContentDescription(isNonPersonalProfile, fileName);
} else {
return itemView.getResources().getString(
- isWorkProfile ? R.string.preview_work_file : R.string.preview_file, fileName);
+ isNonPersonalProfile ? R.string.preview_work_file : R.string.preview_file,
+ fileName);
}
}
diff --git a/src/com/android/documentsui/dirlist/GridDirectoryHolder.java b/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
index 744b0c9d9..2fe0155cb 100644
--- a/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
+++ b/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
@@ -35,33 +35,37 @@ import android.widget.TextView;
import androidx.annotation.RequiresApi;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.IconUtils;
import com.android.documentsui.R;
import com.android.documentsui.base.State;
+import com.android.documentsui.base.UserId;
import com.android.documentsui.ui.Views;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
-final class GridDirectoryHolder extends DocumentHolder {
+import java.util.Map;
+final class GridDirectoryHolder extends DocumentHolder {
final TextView mTitle;
private final ImageView mIconCheck;
private final ImageView mIconMime;
- private final ImageView mIconBriefcase;
+ private final ImageView mIconBadge;
private final View mIconLayout;
- public GridDirectoryHolder(Context context, ViewGroup parent) {
+ GridDirectoryHolder(Context context, ViewGroup parent) {
super(context, parent, R.layout.item_dir_grid);
mIconLayout = itemView.findViewById(R.id.icon);
mTitle = (TextView) itemView.findViewById(android.R.id.title);
mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime_sm);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
- mIconBriefcase = (ImageView) itemView.findViewById(R.id.icon_briefcase);
+ mIconBadge = (ImageView) itemView.findViewById(R.id.icon_profile_badge);
mIconMime.setImageDrawable(
IconUtils.loadMimeIcon(context, DocumentsContract.Document.MIME_TYPE_DIR));
- if (SdkLevel.isAtLeastT()) {
+ if (SdkLevel.isAtLeastT() && !FeatureFlagUtils.isPrivateSpaceEnabled()) {
setUpdatableWorkProfileIcon(context);
}
}
@@ -71,7 +75,7 @@ final class GridDirectoryHolder extends DocumentHolder {
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
Drawable drawable = dpm.getResources().getDrawable(WORK_PROFILE_ICON, SOLID_COLORED, () ->
context.getDrawable(R.drawable.ic_briefcase));
- mIconBriefcase.setImageDrawable(drawable);
+ mIconBadge.setImageDrawable(drawable);
}
@Override
@@ -90,7 +94,16 @@ final class GridDirectoryHolder extends DocumentHolder {
@Override
public void bindBriefcaseIcon(boolean show) {
- mIconBriefcase.setVisibility(show ? View.VISIBLE : View.GONE);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void bindProfileIcon(boolean show, int userIdIdentifier) {
+ Map<UserId, Drawable> userIdToBadgeMap = DocumentsApplication.getUserManagerState(
+ mContext).getUserIdToBadgeMap();
+ Drawable drawable = userIdToBadgeMap.get(UserId.of(userIdIdentifier));
+ mIconBadge.setImageDrawable(drawable);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
}
@Override
@@ -107,12 +120,13 @@ final class GridDirectoryHolder extends DocumentHolder {
/**
* Bind this view to the given document for display.
- * @param cursor Pointing to the item to be bound.
+ *
+ * @param cursor Pointing to the item to be bound.
* @param modelId The model ID of the item.
*/
@Override
public void bind(Cursor cursor, String modelId) {
- assert(cursor != null);
+ assert (cursor != null);
this.mModelId = modelId;
diff --git a/src/com/android/documentsui/dirlist/GridDocumentHolder.java b/src/com/android/documentsui/dirlist/GridDocumentHolder.java
index 2da538203..a63fba264 100644
--- a/src/com/android/documentsui/dirlist/GridDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/GridDocumentHolder.java
@@ -37,14 +37,17 @@ import android.widget.TextView;
import androidx.annotation.RequiresApi;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.UserId;
import com.android.documentsui.roots.RootCursorWrapper;
import com.android.documentsui.ui.Views;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
+import java.util.Map;
import java.util.function.Function;
final class GridDocumentHolder extends DocumentHolder {
@@ -56,7 +59,7 @@ final class GridDocumentHolder extends DocumentHolder {
final ImageView mIconMimeSm;
final ImageView mIconThumb;
final ImageView mIconCheck;
- final ImageView mIconBriefcase;
+ final ImageView mIconBadge;
final IconHelper mIconHelper;
final View mIconLayout;
final View mPreviewIcon;
@@ -64,7 +67,7 @@ final class GridDocumentHolder extends DocumentHolder {
// This is used in as a convenience in our bind method.
private final DocumentInfo mDoc = new DocumentInfo();
- public GridDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
+ GridDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
super(context, parent, R.layout.item_doc_grid);
mIconLayout = itemView.findViewById(R.id.icon);
@@ -75,12 +78,12 @@ final class GridDocumentHolder extends DocumentHolder {
mIconMimeSm = (ImageView) itemView.findViewById(R.id.icon_mime_sm);
mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
- mIconBriefcase = (ImageView) itemView.findViewById(R.id.icon_briefcase);
+ mIconBadge = (ImageView) itemView.findViewById(R.id.icon_profile_badge);
mPreviewIcon = itemView.findViewById(R.id.preview_icon);
mIconHelper = iconHelper;
- if (SdkLevel.isAtLeastT()) {
+ if (SdkLevel.isAtLeastT() && !FeatureFlagUtils.isPrivateSpaceEnabled()) {
setUpdatableWorkProfileIcon(context);
}
}
@@ -90,7 +93,7 @@ final class GridDocumentHolder extends DocumentHolder {
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
Drawable drawable = dpm.getResources().getDrawable(WORK_PROFILE_ICON, SOLID_COLORED, () ->
context.getDrawable(R.drawable.ic_briefcase));
- mIconBriefcase.setImageDrawable(drawable);
+ mIconBadge.setImageDrawable(drawable);
}
@Override
@@ -108,7 +111,7 @@ final class GridDocumentHolder extends DocumentHolder {
// But it should be an error to be set to selected && be disabled.
if (!itemView.isEnabled()) {
- assert(!selected);
+ assert (!selected);
}
super.setSelected(selected, animate);
@@ -138,19 +141,28 @@ final class GridDocumentHolder extends DocumentHolder {
mPreviewIcon.setContentDescription(
getPreviewIconContentDescription(
mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier()),
- mDoc.displayName));
+ mDoc.displayName, mDoc.userId));
mPreviewIcon.setAccessibilityDelegate(new PreviewAccessibilityDelegate(clickCallback));
}
}
@Override
public void bindBriefcaseIcon(boolean show) {
- mIconBriefcase.setVisibility(show ? View.VISIBLE : View.GONE);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void bindProfileIcon(boolean show, int userIdIdentifier) {
+ Map<UserId, Drawable> userIdToBadgeMap = DocumentsApplication.getUserManagerState(
+ mContext).getUserIdToBadgeMap();
+ Drawable drawable = userIdToBadgeMap.get(UserId.of(userIdIdentifier));
+ mIconBadge.setImageDrawable(drawable);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
}
@Override
public boolean inDragRegion(MotionEvent event) {
- // Entire grid box should be draggable
+ // Entire grid box should be draggable
return true;
}
@@ -166,12 +178,13 @@ final class GridDocumentHolder extends DocumentHolder {
/**
* Bind this view to the given document for display.
- * @param cursor Pointing to the item to be bound.
+ *
+ * @param cursor Pointing to the item to be bound.
* @param modelId The model ID of the item.
*/
@Override
public void bind(Cursor cursor, String modelId) {
- assert(cursor != null);
+ assert (cursor != null);
mModelId = modelId;
diff --git a/src/com/android/documentsui/dirlist/GridPhotoHolder.java b/src/com/android/documentsui/dirlist/GridPhotoHolder.java
index 06185068d..84aab3c20 100644
--- a/src/com/android/documentsui/dirlist/GridPhotoHolder.java
+++ b/src/com/android/documentsui/dirlist/GridPhotoHolder.java
@@ -36,14 +36,17 @@ import android.widget.ImageView;
import androidx.annotation.RequiresApi;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.UserId;
import com.android.documentsui.roots.RootCursorWrapper;
import com.android.documentsui.ui.Views;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
+import java.util.Map;
import java.util.function.Function;
final class GridPhotoHolder extends DocumentHolder {
@@ -53,23 +56,23 @@ final class GridPhotoHolder extends DocumentHolder {
private final ImageView mIconCheck;
private final IconHelper mIconHelper;
private final View mPreviewIcon;
- private final View mIconBriefcase;
+ private final View mIconBadge;
// This is used in as a convenience in our bind method.
private final DocumentInfo mDoc = new DocumentInfo();
- public GridPhotoHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
+ GridPhotoHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
super(context, parent, R.layout.item_photo_grid);
mIconMimeLg = (ImageView) itemView.findViewById(R.id.icon_mime_lg);
mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
- mIconBriefcase = itemView.findViewById(R.id.icon_briefcase);
+ mIconBadge = itemView.findViewById(R.id.icon_profile_badge);
mPreviewIcon = itemView.findViewById(R.id.preview_icon);
mIconHelper = iconHelper;
- if (SdkLevel.isAtLeastT()) {
+ if (SdkLevel.isAtLeastT() && !FeatureFlagUtils.isPrivateSpaceEnabled()) {
setUpdatableWorkProfileIcon(context);
}
}
@@ -80,7 +83,7 @@ final class GridPhotoHolder extends DocumentHolder {
Drawable drawable = dpm.getResources().getDrawable(
WORK_PROFILE_ICON, SOLID_NOT_COLORED, () ->
context.getDrawable(R.drawable.ic_briefcase));
- ImageView icon = (ImageView) mIconBriefcase.findViewById(R.id.icon_id);
+ ImageView icon = (ImageView) mIconBadge.findViewById(R.id.icon_id);
icon.setImageDrawable(drawable);
}
@@ -123,14 +126,24 @@ final class GridPhotoHolder extends DocumentHolder {
mPreviewIcon.setContentDescription(
getPreviewIconContentDescription(
mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier()),
- mDoc.displayName));
+ mDoc.displayName, mDoc.userId));
mPreviewIcon.setAccessibilityDelegate(new PreviewAccessibilityDelegate(clickCallback));
}
}
@Override
public void bindBriefcaseIcon(boolean show) {
- mIconBriefcase.setVisibility(show ? View.VISIBLE : View.GONE);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void bindProfileIcon(boolean show, int userIdIdentifier) {
+ Map<UserId, Drawable> userIdToBadgeMap = DocumentsApplication.getUserManagerState(
+ mContext).getUserIdToBadgeMap();
+ Drawable drawable = userIdToBadgeMap.get(UserId.of(userIdIdentifier));
+ ImageView icon = mIconBadge.findViewById(R.id.icon_id);
+ icon.setImageDrawable(drawable);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
}
@Override
@@ -152,7 +165,8 @@ final class GridPhotoHolder extends DocumentHolder {
/**
* Bind this view to the given document for display.
- * @param cursor Pointing to the item to be bound.
+ *
+ * @param cursor Pointing to the item to be bound.
* @param modelId The model ID of the item.
*/
@Override
diff --git a/src/com/android/documentsui/dirlist/HeaderMessageDocumentHolder.java b/src/com/android/documentsui/dirlist/HeaderMessageDocumentHolder.java
index bb98894a3..b62605954 100644
--- a/src/com/android/documentsui/dirlist/HeaderMessageDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/HeaderMessageDocumentHolder.java
@@ -45,7 +45,7 @@ final class HeaderMessageDocumentHolder extends MessageHolder {
private final Button mActionButton;
private Message mMessage;
- public HeaderMessageDocumentHolder(Context context, ViewGroup parent) {
+ HeaderMessageDocumentHolder(Context context, ViewGroup parent) {
super(context, parent, R.layout.item_doc_header_message);
mRoot = itemView.findViewById(R.id.item_root);
diff --git a/src/com/android/documentsui/dirlist/IconHelper.java b/src/com/android/documentsui/dirlist/IconHelper.java
index e2c990583..9a650b32c 100644
--- a/src/com/android/documentsui/dirlist/IconHelper.java
+++ b/src/com/android/documentsui/dirlist/IconHelper.java
@@ -20,6 +20,7 @@ import static com.android.documentsui.base.SharedMinimal.VERBOSE;
import static com.android.documentsui.base.State.MODE_GRID;
import static com.android.documentsui.base.State.MODE_LIST;
+import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
@@ -41,11 +42,13 @@ import com.android.documentsui.R;
import com.android.documentsui.ThumbnailCache;
import com.android.documentsui.ThumbnailCache.Result;
import com.android.documentsui.ThumbnailLoader;
+import com.android.documentsui.UserManagerState;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.MimeTypes;
import com.android.documentsui.base.State;
import com.android.documentsui.base.State.ViewMode;
import com.android.documentsui.base.UserId;
+import com.android.documentsui.util.FeatureFlagUtils;
import java.util.function.BiConsumer;
@@ -66,31 +69,33 @@ public class IconHelper {
private final boolean mMaybeShowBadge;
@Nullable
private final UserId mManagedUser;
+ private final UserManagerState mUserManagerState;
/**
- * @param context
* @param mode MODE_GRID or MODE_LIST
*/
public IconHelper(Context context, int mode, boolean maybeShowBadge) {
this(context, mode, maybeShowBadge, DocumentsApplication.getThumbnailCache(context),
- DocumentsApplication.getUserIdManager(context).getManagedUser());
+ FeatureFlagUtils.isPrivateSpaceEnabled() ? null
+ : DocumentsApplication.getUserIdManager(context).getManagedUser(),
+ FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? DocumentsApplication.getUserManagerState(context) : null);
}
@VisibleForTesting
IconHelper(Context context, int mode, boolean maybeShowBadge, ThumbnailCache thumbnailCache,
- @Nullable UserId managedUser) {
+ @Nullable UserId managedUser, @Nullable UserManagerState userManagerState) {
mContext = context;
setViewMode(mode);
mThumbnailCache = thumbnailCache;
mManagedUser = managedUser;
mMaybeShowBadge = maybeShowBadge;
+ mUserManagerState = userManagerState;
}
/**
* Enables or disables thumbnails. When thumbnails are disabled, mime icons (or custom icons, if
* specified by the document) are used instead.
- *
- * @param enabled
*/
public void setThumbnailsEnabled(boolean enabled) {
mThumbnailsEnabled = enabled;
@@ -125,8 +130,6 @@ public class IconHelper {
/**
* Cancels any ongoing load operations associated with the given ImageView.
- *
- * @param icon
*/
public void stopLoading(ImageView icon) {
final ThumbnailLoader oldTask = (ThumbnailLoader) icon.getTag();
@@ -139,11 +142,10 @@ public class IconHelper {
/**
* Load thumbnails for a directory list item.
*
- * @param doc The document
- * @param iconThumb The itemview's thumbnail icon.
- * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
+ * @param doc The document
+ * @param iconThumb The itemview's thumbnail icon.
+ * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
* @param subIconMime The second itemview's mime icon. Always visible.
- * @return
*/
public void load(
DocumentInfo doc,
@@ -157,15 +159,14 @@ public class IconHelper {
/**
* Load thumbnails for a directory list item.
*
- * @param uri The URI for the file being represented.
- * @param mimeType The mime type of the file being represented.
- * @param docFlags Flags for the file being represented.
- * @param docIcon Custom icon (if any) for the file being requested.
+ * @param uri The URI for the file being represented.
+ * @param mimeType The mime type of the file being represented.
+ * @param docFlags Flags for the file being represented.
+ * @param docIcon Custom icon (if any) for the file being requested.
* @param docLastModified the last modified value of the file being requested.
- * @param iconThumb The itemview's thumbnail icon.
- * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
- * @param subIconMime The second itemview's mime icon. Always visible.
- * @return
+ * @param iconThumb The itemview's thumbnail icon.
+ * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
+ * @param subIconMime The second itemview's mime icon. Always visible.
*/
public void load(Uri uri, UserId userId, String mimeType, int docFlags, int docIcon,
long docLastModified, ImageView iconThumb, ImageView iconMime,
@@ -180,7 +181,7 @@ public class IconHelper {
final boolean showThumbnail = supportsThumbnail && allowThumbnail && mThumbnailsEnabled;
if (showThumbnail) {
loadedThumbnail =
- loadThumbnail(uri, userId, docAuthority, docLastModified, iconThumb, iconMime);
+ loadThumbnail(uri, userId, docAuthority, docLastModified, iconThumb, iconMime);
}
final Drawable mimeIcon = getDocumentIcon(mContext, userId, docAuthority,
@@ -207,9 +208,11 @@ public class IconHelper {
iconThumb.setImageBitmap(cachedThumbnail);
boolean stale = (docLastModified > result.getLastModified());
- if (VERBOSE) Log.v(TAG,
- String.format("Load thumbnail for %s, got result %d and stale %b.",
- uri.toString(), result.getStatus(), stale));
+ if (VERBOSE) {
+ Log.v(TAG,
+ String.format("Load thumbnail for %s, got result %d and stale %b.",
+ uri.toString(), result.getStatus(), stale));
+ }
if (!result.isExactHit() || stale) {
final BiConsumer<View, View> animator =
(cachedThumbnail == null ? ThumbnailLoader.ANIM_FADE_IN :
@@ -264,6 +267,11 @@ public class IconHelper {
* Returns true if we should show a briefcase icon for the given user.
*/
public boolean shouldShowBadge(int userIdIdentifier) {
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ return mMaybeShowBadge
+ && mUserManagerState.getUserIds().size() > 1
+ && ActivityManager.getCurrentUser() != userIdIdentifier;
+ }
return mMaybeShowBadge && mManagedUser != null
&& mManagedUser.getIdentifier() == userIdIdentifier;
}
diff --git a/src/com/android/documentsui/dirlist/InflateMessageDocumentHolder.java b/src/com/android/documentsui/dirlist/InflateMessageDocumentHolder.java
index 2e2b92128..f20bb6eeb 100644
--- a/src/com/android/documentsui/dirlist/InflateMessageDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/InflateMessageDocumentHolder.java
@@ -52,7 +52,7 @@ final class InflateMessageDocumentHolder extends MessageHolder {
private View mCrossProfileContent;
private ProgressBar mCrossProfileProgress;
- public InflateMessageDocumentHolder(Context context, ViewGroup parent) {
+ InflateMessageDocumentHolder(Context context, ViewGroup parent) {
super(context, parent, R.layout.item_doc_inflated_message);
mContentView = itemView.findViewById(R.id.content);
mCrossProfileView = itemView.findViewById(R.id.cross_profile);
diff --git a/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index 96c49e047..dbeeb94f0 100644
--- a/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -40,6 +40,7 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Lookup;
@@ -48,9 +49,11 @@ import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
import com.android.documentsui.roots.RootCursorWrapper;
import com.android.documentsui.ui.Views;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
import java.util.ArrayList;
+import java.util.Map;
import java.util.function.Function;
final class ListDocumentHolder extends DocumentHolder {
@@ -67,7 +70,7 @@ final class ListDocumentHolder extends DocumentHolder {
private final ImageView mIconMime;
private final ImageView mIconThumb;
private final ImageView mIconCheck;
- private final ImageView mIconBriefcase;
+ private final ImageView mIconBadge;
private final View mIconLayout;
final View mPreviewIcon;
@@ -84,7 +87,7 @@ final class ListDocumentHolder extends DocumentHolder {
mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
- mIconBriefcase = (ImageView) itemView.findViewById(R.id.icon_briefcase);
+ mIconBadge = (ImageView) itemView.findViewById(R.id.icon_profile_badge);
mTitle = (TextView) itemView.findViewById(android.R.id.title);
mSize = (TextView) itemView.findViewById(R.id.size);
mDate = (TextView) itemView.findViewById(R.id.date);
@@ -98,7 +101,7 @@ final class ListDocumentHolder extends DocumentHolder {
mFileTypeLookup = fileTypeLookup;
mDoc = new DocumentInfo();
- if (SdkLevel.isAtLeastT()) {
+ if (SdkLevel.isAtLeastT() && !FeatureFlagUtils.isPrivateSpaceEnabled()) {
setUpdatableWorkProfileIcon(context);
}
}
@@ -108,7 +111,7 @@ final class ListDocumentHolder extends DocumentHolder {
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
Drawable drawable = dpm.getResources().getDrawable(WORK_PROFILE_ICON, SOLID_COLORED, () ->
context.getDrawable(R.drawable.ic_briefcase));
- mIconBriefcase.setImageDrawable(drawable);
+ mIconBadge.setImageDrawable(drawable);
}
@Override
@@ -158,7 +161,7 @@ final class ListDocumentHolder extends DocumentHolder {
mPreviewIcon.setContentDescription(
getPreviewIconContentDescription(
mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier()),
- mDoc.displayName));
+ mDoc.displayName, mDoc.userId));
mPreviewIcon.setAccessibilityDelegate(
new PreviewAccessibilityDelegate(clickCallback));
}
@@ -167,7 +170,16 @@ final class ListDocumentHolder extends DocumentHolder {
@Override
public void bindBriefcaseIcon(boolean show) {
- mIconBriefcase.setVisibility(show ? View.VISIBLE : View.GONE);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void bindProfileIcon(boolean show, int userIdIdentifier) {
+ Map<UserId, Drawable> userIdToBadgeMap = DocumentsApplication.getUserManagerState(
+ mContext).getUserIdToBadgeMap();
+ Drawable drawable = userIdToBadgeMap.get(UserId.of(userIdIdentifier));
+ mIconBadge.setImageDrawable(drawable);
+ mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE);
}
@Override
@@ -209,7 +221,7 @@ final class ListDocumentHolder extends DocumentHolder {
/**
* Bind this view to the given document for display.
*
- * @param cursor Pointing to the item to be bound.
+ * @param cursor Pointing to the item to be bound.
* @param modelId The model ID of the item.
*/
@Override
diff --git a/src/com/android/documentsui/dirlist/Message.java b/src/com/android/documentsui/dirlist/Message.java
index ccdad461a..37837e00f 100644
--- a/src/com/android/documentsui/dirlist/Message.java
+++ b/src/com/android/documentsui/dirlist/Message.java
@@ -35,8 +35,13 @@ import android.Manifest;
import android.app.AuthenticationRequiredException;
import android.app.admin.DevicePolicyManager;
import android.content.pm.PackageManager;
+import android.content.pm.UserProperties;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -52,13 +57,20 @@ import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
import com.android.documentsui.dirlist.DocumentsAdapter.Environment;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
/**
* Data object used by {@link InflateMessageDocumentHolder} and {@link HeaderMessageDocumentHolder}.
*/
abstract class Message {
+ private static final int ACCESS_CROSS_PROFILE_FILES = -1;
+
protected final Environment mEnv;
// If the message has a button, this will be the default button call back.
protected final Runnable mDefaultCallback;
@@ -78,7 +90,7 @@ abstract class Message {
mDefaultCallback = defaultCallback;
}
- abstract void update(Update Event);
+ abstract void update(Update event);
protected void update(@Nullable CharSequence messageTitle, CharSequence messageString,
@Nullable CharSequence buttonString, Drawable icon) {
@@ -121,6 +133,7 @@ abstract class Message {
/**
* Return this message should keep showing or not.
+ *
* @return true if this message should keep showing.
*/
boolean shouldKeep() {
@@ -173,7 +186,7 @@ abstract class Message {
}
private void updateToAuthenticationExceptionHeader(Update event) {
- assert(mEnv.getFeatures().isRemoteActionsEnabled());
+ assert (mEnv.getFeatures().isRemoteActionsEnabled());
RootInfo root = mEnv.getDisplayState().stack.getRoot();
String appName = DocumentsApplication.getProvidersCache(
@@ -199,7 +212,12 @@ abstract class Message {
final static class InflateMessage extends Message {
+ private static final String TAG = "InflateMessage";
+ private UserId mSourceUserId = null;
+ private UserId mSelectedUserId = null;
+ private Map<UserId, String> mUserIdToLabelMap = new HashMap<>();
private final boolean mCanModifyQuietMode;
+ private UserManager mUserManager = null;
InflateMessage(Environment env, Runnable callback) {
super(env, callback);
@@ -208,6 +226,38 @@ abstract class Message {
== PackageManager.PERMISSION_GRANTED;
}
+ InflateMessage(Environment env, Runnable callback, UserId sourceUserId,
+ UserId selectedUserId, Map<UserId, String> userIdToLabelMap,
+ UserManager userManager) {
+ super(env, callback);
+ mSourceUserId = sourceUserId;
+ mSelectedUserId = selectedUserId;
+ mUserIdToLabelMap = userIdToLabelMap;
+ mUserManager = userManager != null ? userManager
+ : mEnv.getContext().getSystemService(UserManager.class);
+ mCanModifyQuietMode = setCanModifyQuietMode();
+ }
+
+ private boolean setCanModifyQuietMode() {
+ if (SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ if (mUserManager == null) {
+ Log.e(TAG, "can not obtain user manager class");
+ return false;
+ }
+
+ UserProperties userProperties = mUserManager.getUserProperties(
+ UserHandle.of(mSelectedUserId.getIdentifier()));
+ return userProperties.getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_PAUSED
+ && mEnv.getContext().checkSelfPermission(
+ Manifest.permission.MODIFY_QUIET_MODE)
+ == PackageManager.PERMISSION_GRANTED;
+ } else {
+ return mEnv.getContext().checkSelfPermission(Manifest.permission.MODIFY_QUIET_MODE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ }
+
@Override
void update(Update event) {
reset();
@@ -233,16 +283,29 @@ abstract class Message {
private void updateToQuietModeErrorMessage(UserId userId) {
mLayout = InflateMessageDocumentHolder.LAYOUT_CROSS_PROFILE_ERROR;
- CharSequence buttonText = null;
+ String buttonText = null;
+ Resources res = null;
+ String selectedProfile = null;
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ res = mEnv.getContext().getResources();
+ assert mUserIdToLabelMap != null;
+ selectedProfile = mUserIdToLabelMap.get(userId);
+ }
if (mCanModifyQuietMode) {
- buttonText = getEnterpriseString(
- WORK_PROFILE_OFF_ENABLE_BUTTON, R.string.quiet_mode_button);
+ buttonText = FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? res.getString(R.string.profile_quiet_mode_button,
+ selectedProfile.toLowerCase(Locale.getDefault()))
+ : getEnterpriseString(
+ WORK_PROFILE_OFF_ENABLE_BUTTON, R.string.quiet_mode_button);
mCallback = () -> mEnv.getActionHandler().requestQuietModeDisabled(
mEnv.getDisplayState().stack.getRoot(), userId);
}
- update(
- getEnterpriseString(
- WORK_PROFILE_OFF_ERROR_TITLE, R.string.quiet_mode_error_title),
+
+ update(FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? res.getString(R.string.profile_quiet_mode_error_title,
+ selectedProfile)
+ : getEnterpriseString(
+ WORK_PROFILE_OFF_ERROR_TITLE, R.string.quiet_mode_error_title),
/* messageString= */ "",
buttonText,
getWorkProfileOffIcon());
@@ -257,58 +320,117 @@ abstract class Message {
}
private CharSequence getCrossProfileNoPermissionErrorTitle() {
- boolean currentUserIsSystem = UserId.CURRENT_USER.isSystem();
switch (mEnv.getDisplayState().action) {
case State.ACTION_GET_CONTENT:
case State.ACTION_OPEN:
case State.ACTION_OPEN_TREE:
- return currentUserIsSystem
- ? getEnterpriseString(
- CANT_SELECT_WORK_FILES_TITLE,
- R.string.cant_select_work_files_error_title)
- : getEnterpriseString(
- CANT_SELECT_PERSONAL_FILES_TITLE,
- R.string.cant_select_personal_files_error_title);
+ return FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? getErrorTitlePrivateSpaceEnabled(ACCESS_CROSS_PROFILE_FILES)
+ : getErrorTitlePrivateSpaceDisabled(ACCESS_CROSS_PROFILE_FILES);
case State.ACTION_CREATE:
- return currentUserIsSystem
- ? getEnterpriseString(
- CANT_SAVE_TO_WORK_TITLE, R.string.cant_save_to_work_error_title)
- : getEnterpriseString(
- CANT_SAVE_TO_PERSONAL_TITLE,
- R.string.cant_save_to_personal_error_title);
+ return FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? getErrorTitlePrivateSpaceEnabled(State.ACTION_CREATE)
+ : getErrorTitlePrivateSpaceDisabled(State.ACTION_CREATE);
}
return getEnterpriseString(
CROSS_PROFILE_NOT_ALLOWED_TITLE,
R.string.cross_profile_action_not_allowed_title);
}
- private CharSequence getCrossProfileNoPermissionErrorMessage() {
+ private CharSequence getErrorTitlePrivateSpaceEnabled(int action) {
+ Resources res = mEnv.getContext().getResources();
+ String selectedProfileLabel = mUserIdToLabelMap.get(mSelectedUserId);
+ if (selectedProfileLabel == null) return "";
+ if (action == ACCESS_CROSS_PROFILE_FILES) {
+ return res.getString(R.string.cant_select_cross_profile_files_error_title,
+ selectedProfileLabel.toLowerCase(Locale.getDefault()));
+ } else if (action == State.ACTION_CREATE) {
+ return res.getString(R.string.cant_save_to_cross_profile_error_title,
+ selectedProfileLabel.toLowerCase(Locale.getDefault()));
+ } else {
+ Log.e(TAG, "Unexpected intent action received.");
+ return "";
+ }
+ }
+
+ private CharSequence getErrorTitlePrivateSpaceDisabled(int action) {
boolean currentUserIsSystem = UserId.CURRENT_USER.isSystem();
+ if (action == ACCESS_CROSS_PROFILE_FILES) {
+ return currentUserIsSystem
+ ? getEnterpriseString(CANT_SELECT_WORK_FILES_TITLE,
+ R.string.cant_select_work_files_error_title)
+ : getEnterpriseString(CANT_SELECT_PERSONAL_FILES_TITLE,
+ R.string.cant_select_personal_files_error_title);
+ } else if (action == State.ACTION_CREATE) {
+ return currentUserIsSystem
+ ? getEnterpriseString(CANT_SAVE_TO_WORK_TITLE,
+ R.string.cant_save_to_work_error_title)
+ : getEnterpriseString(CANT_SAVE_TO_PERSONAL_TITLE,
+ R.string.cant_save_to_personal_error_title);
+ } else {
+ Log.e(TAG, "Unexpected intent action received.");
+ return "";
+ }
+ }
+
+ private CharSequence getCrossProfileNoPermissionErrorMessage() {
switch (mEnv.getDisplayState().action) {
case State.ACTION_GET_CONTENT:
case State.ACTION_OPEN:
case State.ACTION_OPEN_TREE:
- return currentUserIsSystem
- ? getEnterpriseString(
- CANT_SELECT_WORK_FILES_MESSAGE,
- R.string.cant_select_work_files_error_message)
- : getEnterpriseString(
- CANT_SELECT_PERSONAL_FILES_MESSAGE,
- R.string.cant_select_personal_files_error_message);
+ return FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? getErrorMessagePrivateSpaceEnabled(ACCESS_CROSS_PROFILE_FILES)
+ : getErrorMessagePrivateSpaceDisabled(ACCESS_CROSS_PROFILE_FILES);
case State.ACTION_CREATE:
- return currentUserIsSystem
- ? getEnterpriseString(
- CANT_SAVE_TO_WORK_MESSAGE,
- R.string.cant_save_to_work_error_message)
- : getEnterpriseString(
- CANT_SAVE_TO_PERSONAL_MESSAGE,
- R.string.cant_save_to_personal_error_message);
+ return FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? getErrorMessagePrivateSpaceEnabled(State.ACTION_CREATE)
+ : getErrorMessagePrivateSpaceDisabled(State.ACTION_CREATE);
+
}
return getEnterpriseString(
CROSS_PROFILE_NOT_ALLOWED_MESSAGE,
R.string.cross_profile_action_not_allowed_message);
}
+ private CharSequence getErrorMessagePrivateSpaceEnabled(int action) {
+ Resources res = mEnv.getContext().getResources();
+ String sourceProfileLabel = mUserIdToLabelMap.get(mSourceUserId);
+ String selectedProfileLabel = mUserIdToLabelMap.get(mSelectedUserId);
+ if (sourceProfileLabel == null || selectedProfileLabel == null) return "";
+ if (action == ACCESS_CROSS_PROFILE_FILES) {
+ return res.getString(R.string.cant_select_cross_profile_files_error_message,
+ selectedProfileLabel.toLowerCase(Locale.getDefault()),
+ sourceProfileLabel.toLowerCase(Locale.getDefault()));
+ } else if (action == State.ACTION_CREATE) {
+ return res.getString(R.string.cant_save_to_cross_profile_error_message,
+ sourceProfileLabel.toLowerCase(Locale.getDefault()),
+ selectedProfileLabel.toLowerCase(Locale.getDefault()));
+ } else {
+ Log.e(TAG, "Unexpected intent action received.");
+ return "";
+ }
+ }
+
+ private CharSequence getErrorMessagePrivateSpaceDisabled(int action) {
+ boolean currentUserIsSystem = UserId.CURRENT_USER.isSystem();
+ if (action == ACCESS_CROSS_PROFILE_FILES) {
+ return currentUserIsSystem
+ ? getEnterpriseString(CANT_SELECT_WORK_FILES_MESSAGE,
+ R.string.cant_select_work_files_error_message)
+ : getEnterpriseString(CANT_SELECT_PERSONAL_FILES_MESSAGE,
+ R.string.cant_select_personal_files_error_message);
+ } else if (action == State.ACTION_CREATE) {
+ return currentUserIsSystem
+ ? getEnterpriseString(CANT_SAVE_TO_WORK_MESSAGE,
+ R.string.cant_save_to_work_error_message)
+ : getEnterpriseString(CANT_SAVE_TO_PERSONAL_MESSAGE,
+ R.string.cant_save_to_personal_error_message);
+ } else {
+ Log.e(TAG, "Unexpected intent action received.");
+ return "";
+ }
+ }
+
private void updateToInflatedErrorMessage() {
update(null, mEnv.getContext().getResources().getText(R.string.query_error), null,
mEnv.getContext().getDrawable(R.drawable.hourglass));
diff --git a/src/com/android/documentsui/dirlist/MessageHolder.java b/src/com/android/documentsui/dirlist/MessageHolder.java
index 1d8d7d61c..3d65cef04 100644
--- a/src/com/android/documentsui/dirlist/MessageHolder.java
+++ b/src/com/android/documentsui/dirlist/MessageHolder.java
@@ -24,7 +24,7 @@ import android.widget.Space;
* Base class for all non-Document Holder classes.
*/
abstract class MessageHolder extends DocumentHolder {
- public MessageHolder(Context context, Space space) {
+ MessageHolder(Context context, Space space) {
super(context, space);
}
diff --git a/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
index f76360790..d7bfb5d3f 100644
--- a/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
+++ b/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -35,6 +35,7 @@ import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.State;
import com.android.documentsui.roots.RootCursorWrapper;
+import com.android.documentsui.util.FeatureFlagUtils;
import java.util.ArrayList;
import java.util.List;
@@ -136,14 +137,18 @@ final class ModelBackedDocumentsAdapter extends DocumentsAdapter {
boolean enabled = mEnv.isDocumentEnabled(docMimeType, docFlags);
boolean selected = mEnv.isSelected(modelId);
if (!enabled) {
- assert(!selected);
+ assert (!selected);
}
holder.setEnabled(enabled);
holder.setSelected(mEnv.isSelected(modelId), false);
holder.setAction(mEnv.getDisplayState().action);
holder.bindPreviewIcon(mEnv.getDisplayState().shouldShowPreview() && enabled,
view -> mEnv.getActionHandler().previewItem(holder.getItemDetails()));
- holder.bindBriefcaseIcon(mIconHelper.shouldShowBadge(userIdIdentifier));
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ holder.bindProfileIcon(mIconHelper.shouldShowBadge(userIdIdentifier), userIdIdentifier);
+ } else {
+ holder.bindBriefcaseIcon(mIconHelper.shouldShowBadge(userIdIdentifier));
+ }
mEnv.onBindDocumentHolder(holder, cursor);
}
diff --git a/src/com/android/documentsui/dirlist/TransparentDividerDocumentHolder.java b/src/com/android/documentsui/dirlist/TransparentDividerDocumentHolder.java
index 44efd8090..f382900fe 100644
--- a/src/com/android/documentsui/dirlist/TransparentDividerDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/TransparentDividerDocumentHolder.java
@@ -32,7 +32,7 @@ final class TransparentDividerDocumentHolder extends MessageHolder {
private final int mVisibleHeight;
private State mState;
- public TransparentDividerDocumentHolder(Context context) {
+ TransparentDividerDocumentHolder(Context context) {
super(context, new Space(context));
mVisibleHeight = context.getResources().getDimensionPixelSize(
diff --git a/tests/common/com/android/documentsui/TestUserManagerState.java b/tests/common/com/android/documentsui/TestUserManagerState.java
index 0591770fc..8dbe62a2a 100644
--- a/tests/common/com/android/documentsui/TestUserManagerState.java
+++ b/tests/common/com/android/documentsui/TestUserManagerState.java
@@ -31,8 +31,9 @@ public class TestUserManagerState implements UserManagerState {
public List<UserId> userIds = new ArrayList<>();
public Map<UserId, String> userIdToLabelMap = new HashMap<>();
public Map<UserId, Boolean> canFrowardToProfileIdMap = new HashMap<>();
-
public Map<UserId, Drawable> userIdToBadgeMap = new HashMap<>();
+ public String profileLabel = "Test";
+ public Drawable profileBadge = null;
@Override
public List<UserId> getUserIds() {
@@ -53,4 +54,21 @@ public class TestUserManagerState implements UserManagerState {
public Map<UserId, Boolean> getCanForwardToProfileIdMap(Intent intent) {
return canFrowardToProfileIdMap;
}
+
+ @Override
+ public void onProfileActionStatusChange(String action, UserId userId) {
+ if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)) {
+ userIds.remove(userId);
+ userIdToLabelMap.remove(userId);
+ userIdToBadgeMap.remove(userId);
+ canFrowardToProfileIdMap.put(userId, false);
+ return;
+ }
+ if (!userIds.contains(userId)) {
+ userIds.add(userId);
+ }
+ userIdToLabelMap.put(userId, profileLabel);
+ userIdToBadgeMap.put(userId, profileBadge);
+ canFrowardToProfileIdMap.put(userId, true);
+ }
}
diff --git a/tests/common/com/android/documentsui/testing/TestProvidersAccess.java b/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
index 554c42c7d..5a138ea9f 100644
--- a/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
+++ b/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
@@ -111,7 +111,7 @@ public class TestProvidersAccess implements ProvidersAccess {
INSPECTOR.rootId = InspectorProvider.ROOT_ID;
INSPECTOR.title = "Inspector";
INSPECTOR.flags = Root.FLAG_LOCAL_ONLY
- | Root.FLAG_SUPPORTS_CREATE;
+ | Root.FLAG_SUPPORTS_CREATE;
IMAGE = new RootInfo();
IMAGE.userId = userId;
diff --git a/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java b/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
index 9e12433f7..fd17f72fd 100644
--- a/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
+++ b/tests/functional/com/android/documentsui/ActionCreateDocumentUiTest.java
@@ -93,8 +93,8 @@ public class ActionCreateDocumentUiTest extends DocumentsUiTestBase {
assertThat(uri.getPath()).contains(fileName);
assertThat(resultData.getFlags()).isEqualTo(FLAG_GRANT_READ_URI_PERMISSION
- | FLAG_GRANT_WRITE_URI_PERMISSION
- | FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ | FLAG_GRANT_WRITE_URI_PERMISSION
+ | FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
final boolean deletedSuccessfully =
DocumentsContract.deleteDocument(context.getContentResolver(), uri);
diff --git a/tests/functional/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java b/tests/functional/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java
index bcd1131b7..dcb741721 100644
--- a/tests/functional/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java
+++ b/tests/functional/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java
@@ -16,8 +16,13 @@
package com.android.documentsui.dirlist;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
+import android.content.pm.UserProperties;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
import android.view.ViewGroup;
@@ -29,10 +34,23 @@ import com.android.documentsui.ActionHandler;
import com.android.documentsui.ModelId;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.State;
+import com.android.documentsui.base.UserId;
import com.android.documentsui.testing.TestActionHandler;
import com.android.documentsui.testing.TestEnv;
import com.android.documentsui.testing.TestFileTypeLookup;
+import com.android.documentsui.testing.TestProvidersAccess;
+import com.android.documentsui.testing.UserManagers;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.documentsui.util.VersionUtils;
+import com.android.modules.utils.build.SdkLevel;
+
+import com.google.android.collect.Lists;
+
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.HashMap;
+import java.util.Map;
@MediumTest
public class DirectoryAddonsAdapterTest extends AndroidTestCase {
@@ -43,6 +61,18 @@ public class DirectoryAddonsAdapterTest extends AndroidTestCase {
private DirectoryAddonsAdapter mAdapter;
private ActionHandler mActionHandler;
+ @Parameter(0)
+ public boolean isPrivateSpaceEnabled;
+
+ /**
+ * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+ * private space flag enabled and once with it disabled.
+ */
+ @Parameters(name = "privateSpaceEnabled={0}")
+ public static Iterable<?> data() {
+ return Lists.newArrayList(true, false);
+ }
+
public void setUp() {
mEnv = TestEnv.create(AUTHORITY);
@@ -52,13 +82,36 @@ public class DirectoryAddonsAdapterTest extends AndroidTestCase {
final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY);
DocumentsAdapter.Environment env = new TestEnvironment(testContext, mEnv, mActionHandler);
- mAdapter = new DirectoryAddonsAdapter(
- env,
- new ModelBackedDocumentsAdapter(
- env,
- new IconHelper(testContext, State.MODE_GRID, /* maybeShowBadge= */ false),
- new TestFileTypeLookup()));
-
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ UserId managedUser = UserId.of(100);
+ Map<UserId, String> userIdToLabelMap = new HashMap<>();
+ userIdToLabelMap.put(TestProvidersAccess.USER_ID, "Personal");
+ userIdToLabelMap.put(managedUser, "Work");
+ UserManager userManager = UserManagers.create();
+ if (SdkLevel.isAtLeastV()) {
+ UserProperties managedUserProperties = new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ when(userManager.getUserProperties(UserHandle.of(100)))
+ .thenReturn(managedUserProperties);
+ }
+ mAdapter = new DirectoryAddonsAdapter(
+ env,
+ new ModelBackedDocumentsAdapter(
+ env,
+ new IconHelper(testContext, State.MODE_GRID, /* maybeShowBadge= */
+ false),
+ new TestFileTypeLookup()),
+ TestProvidersAccess.USER_ID, managedUser, userIdToLabelMap, userManager);
+ } else {
+ mAdapter = new DirectoryAddonsAdapter(
+ env,
+ new ModelBackedDocumentsAdapter(
+ env,
+ new IconHelper(testContext, State.MODE_GRID, /* maybeShowBadge= */
+ false),
+ new TestFileTypeLookup()));
+ }
mEnv.model.addUpdateListener(mAdapter.getModelUpdateListener());
}
diff --git a/tests/unit/com/android/documentsui/UserManagerStateTest.java b/tests/unit/com/android/documentsui/UserManagerStateTest.java
index 670cb1da2..f05e186c6 100644
--- a/tests/unit/com/android/documentsui/UserManagerStateTest.java
+++ b/tests/unit/com/android/documentsui/UserManagerStateTest.java
@@ -43,6 +43,7 @@ import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -50,12 +51,6 @@ import java.util.Map;
@SmallTest
public class UserManagerStateTest {
- private static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = 0;
- private static final int SHOW_IN_SHARING_SURFACES_SEPARATE = 1;
- private static final int SHOW_IN_SHARING_SURFACES_NO = 2;
- private static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0;
- private static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1;
-
private final UserHandle mSystemUser = UserHandle.SYSTEM;
private final UserHandle mManagedUser = UserHandle.of(100);
private final UserHandle mPrivateUser = UserHandle.of(101);
@@ -83,29 +78,31 @@ public class UserManagerStateTest {
if (SdkLevel.isAtLeastV()) {
UserProperties systemUserProperties = new UserProperties.Builder()
- .setShowInSharingSurfaces(SHOW_IN_SHARING_SURFACES_SEPARATE)
+ .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileContentSharingStrategy(
- CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
.build();
UserProperties managedUserProperties = new UserProperties.Builder()
- .setShowInSharingSurfaces(SHOW_IN_SHARING_SURFACES_SEPARATE)
+ .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileContentSharingStrategy(
- CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
.build();
UserProperties privateUserProperties = new UserProperties.Builder()
- .setShowInSharingSurfaces(SHOW_IN_SHARING_SURFACES_SEPARATE)
+ .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileContentSharingStrategy(
- CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
.build();
UserProperties otherUserProperties = new UserProperties.Builder()
- .setShowInSharingSurfaces(SHOW_IN_SHARING_SURFACES_WITH_PARENT)
+ .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
.setCrossProfileContentSharingStrategy(
- CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
.build();
UserProperties normalUserProperties = new UserProperties.Builder()
- .setShowInSharingSurfaces(SHOW_IN_SHARING_SURFACES_NO)
+ .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_NO)
.setCrossProfileContentSharingStrategy(
- CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
.build();
when(mMockUserManager.getUserProperties(mSystemUser)).thenReturn(systemUserProperties);
when(mMockUserManager.getUserProperties(mManagedUser)).thenReturn(
@@ -562,6 +559,118 @@ public class UserManagerStateTest {
.isEqualTo(expectedCanForwardToProfileIdMap);
}
+ @Test
+ public void testOnProfileStatusChange_anyIntentActionOnManagedProfile() {
+ if (!SdkLevel.isAtLeastV()) return;
+ UserId currentUser = UserId.of(mSystemUser);
+ initializeUserManagerState(currentUser,
+ Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
+
+ // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will empty
+ // by default if the getters of these member variables have not been called
+ List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
+ Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
+ mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));
+
+ String action = "any_intent";
+ mUserManagerState.onProfileActionStatusChange(action, UserId.of(mManagedUser));
+
+ assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
+ .that(mUserManagerState.getUserIds()).isEqualTo(userIdsBeforeIntent);
+ assertWithMessage(
+ "Unexpected changes to canForwardToProfileIdMap on receiving intent: " + action)
+ .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
+ canForwardToProfileIdMapBeforeIntent);
+ }
+
+ @Test
+ public void testOnProfileStatusChange_actionProfileUnavailableOnPrivateProfile() {
+ if (!SdkLevel.isAtLeastV() || !FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ UserId currentUser = UserId.of(mSystemUser);
+ UserId managedUser = UserId.of(mManagedUser);
+ UserId privateUser = UserId.of(mPrivateUser);
+ final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
+ when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
+ PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
+ mMockResolveInfoList);
+ initializeUserManagerState(currentUser,
+ Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
+
+ // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will empty
+ // by default if the getters of these member variables have not been called
+ List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
+ Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
+ mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));
+
+ List<UserId> expectedUserIdsAfterIntent = Lists.newArrayList(currentUser, managedUser);
+ Map<UserId, Boolean> expectedCanForwardToProfileIdMapAfterIntent = new HashMap<>();
+ expectedCanForwardToProfileIdMapAfterIntent.put(currentUser, true);
+ expectedCanForwardToProfileIdMapAfterIntent.put(managedUser, true);
+
+ String action = Intent.ACTION_PROFILE_UNAVAILABLE;
+ mUserManagerState.onProfileActionStatusChange(action, privateUser);
+
+ assertWithMessage(
+ "UserIds list should not be same before and after receiving intent: " + action)
+ .that(mUserManagerState.getUserIds()).isNotEqualTo(userIdsBeforeIntent);
+ assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
+ .that(mUserManagerState.getUserIds()).isEqualTo(expectedUserIdsAfterIntent);
+ assertWithMessage(
+ "CanForwardToLabelMap should not be same before and after receiving intent: "
+ + action)
+ .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isNotEqualTo(
+ canForwardToProfileIdMapBeforeIntent);
+ assertWithMessage(
+ "Unexpected changes to canForwardToProfileIdMap on receiving intent: " + action)
+ .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
+ expectedCanForwardToProfileIdMapAfterIntent);
+ }
+
+ @Test
+ public void testOnProfileStatusChange_actionProfileAvailableOnPrivateProfile() {
+ if (!SdkLevel.isAtLeastV() || !FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ UserId currentUser = UserId.of(mSystemUser);
+ UserId managedUser = UserId.of(mManagedUser);
+ UserId privateUser = UserId.of(mPrivateUser);
+ final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
+ when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
+ PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
+ mMockResolveInfoList);
+ initializeUserManagerState(currentUser,
+ Lists.newArrayList(mSystemUser, mManagedUser));
+
+ // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will empty
+ // by default if the getters of these member variables have not been called
+ List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
+ Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
+ mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));
+
+ List<UserId> expectedUserIdsAfterIntent = Lists.newArrayList(currentUser, managedUser,
+ privateUser);
+ Map<UserId, Boolean> expectedCanForwardToProfileIdMapAfterIntent = new HashMap<>();
+ expectedCanForwardToProfileIdMapAfterIntent.put(currentUser, true);
+ expectedCanForwardToProfileIdMapAfterIntent.put(managedUser, true);
+ expectedCanForwardToProfileIdMapAfterIntent.put(privateUser, true);
+
+ String action = Intent.ACTION_PROFILE_AVAILABLE;
+ mUserManagerState.onProfileActionStatusChange(action, privateUser);
+
+ assertWithMessage(
+ "UserIds list should not be same before and after receiving intent: " + action)
+ .that(mUserManagerState.getUserIds()).isNotEqualTo(userIdsBeforeIntent);
+ assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
+ .that(mUserManagerState.getUserIds()).isEqualTo(expectedUserIdsAfterIntent);
+ assertWithMessage(
+ "CanForwardToLabelMap should not be same before and after receiving intent: "
+ + action)
+ .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isNotEqualTo(
+ canForwardToProfileIdMapBeforeIntent);
+ assertWithMessage(
+ "Unexpected changes to canForwardToProfileIdMap on receiving intent: " + action)
+ .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
+ expectedCanForwardToProfileIdMapAfterIntent);
+ }
+
private void initializeUserManagerState(UserId current, List<UserHandle> usersOnDevice) {
when(mMockUserManager.getUserProfiles()).thenReturn(usersOnDevice);
mUserManagerState = new UserManagerState.RuntimeUserManagerState(mMockContext, current,
diff --git a/tests/unit/com/android/documentsui/dirlist/AppsRowManagerTest.java b/tests/unit/com/android/documentsui/dirlist/AppsRowManagerTest.java
index 6482440da..fc78def0e 100644
--- a/tests/unit/com/android/documentsui/dirlist/AppsRowManagerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/AppsRowManagerTest.java
@@ -36,6 +36,7 @@ import com.android.documentsui.ActionHandler;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.R;
import com.android.documentsui.TestUserIdManager;
+import com.android.documentsui.TestUserManagerState;
import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
import com.android.documentsui.sidebar.AppItem;
@@ -44,16 +45,22 @@ import com.android.documentsui.sidebar.RootItem;
import com.android.documentsui.testing.TestActionHandler;
import com.android.documentsui.testing.TestProvidersAccess;
import com.android.documentsui.testing.TestResolveInfo;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.documentsui.util.VersionUtils;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
import java.util.ArrayList;
import java.util.List;
+@RunWith(Parameterized.class)
public class AppsRowManagerTest {
private AppsRowManager mAppsRowManager;
@@ -62,17 +69,31 @@ public class AppsRowManagerTest {
private boolean mMaybeShowBadge;
private BaseActivity mActivity;
private TestUserIdManager mTestUserIdManager;
+ private TestUserManagerState mTestUserManagerState;
private State mState;
private View mAppsRow;
private LinearLayout mAppsGroup;
+ @Parameter(0)
+ public boolean isPrivateSpaceEnabled;
+
+ /**
+ * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+ * private space flag enabled and once with it disabled.
+ */
+ @Parameters(name = "privateSpaceEnabled={0}")
+ public static Iterable<?> data() {
+ return com.google.android.collect.Lists.newArrayList(true, false);
+ }
+
@Before
public void setUp() {
mActionHandler = new TestActionHandler();
mTestUserIdManager = new TestUserIdManager();
+ mTestUserManagerState = new TestUserManagerState();
- mAppsRowManager = new AppsRowManager(mActionHandler, mMaybeShowBadge, mTestUserIdManager);
+ mAppsRowManager = getAppsRowManager();
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
@@ -86,9 +107,20 @@ public class AppsRowManagerTest {
when(mActivity.findViewById(R.id.apps_row)).thenReturn(mAppsRow);
when(mActivity.findViewById(R.id.apps_group)).thenReturn(mAppsGroup);
when(mActivity.getSelectedUser()).thenReturn(TestProvidersAccess.USER_ID);
+ }
+ private AppsRowManager getAppsRowManager() {
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ mTestUserManagerState = new TestUserManagerState();
+ mTestUserManagerState.userIds =
+ Lists.newArrayList(UserId.DEFAULT_USER, TestProvidersAccess.OtherUser.USER_ID,
+ TestProvidersAccess.AnotherUser.USER_ID);
+ return new AppsRowManager(mActionHandler, mMaybeShowBadge, mTestUserManagerState);
+ }
+ mTestUserIdManager = new TestUserIdManager();
mTestUserIdManager.userIds =
Lists.newArrayList(UserId.DEFAULT_USER, TestProvidersAccess.OtherUser.USER_ID);
+ return new AppsRowManager(mActionHandler, mMaybeShowBadge, mTestUserIdManager);
}
@Test
@@ -105,9 +137,9 @@ public class AppsRowManagerTest {
assertEquals(chipDataList.size(), rootList.size());
assertEquals(TestProvidersAccess.INSPECTOR.title, chipDataList.get(0).getTitle());
- assertEquals(null, chipDataList.get(0).getSummary());
+ assertNull(chipDataList.get(0).getSummary());
assertEquals(TestProvidersAccess.PICKLES.title, chipDataList.get(1).getTitle());
- assertEquals(null, chipDataList.get(1).getSummary());
+ assertNull(chipDataList.get(1).getSummary());
assertEquals(TestProvidersAccess.PICKLES.summary, chipDataList.get(2).getSummary());
assertEquals(TestProvidersAccess.PICKLES.summary, chipDataList.get(3).getSummary());
}
diff --git a/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java b/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
index 2574f73bd..1e48e3387 100644
--- a/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
@@ -44,7 +44,8 @@ public class DocumentHolderTest extends AndroidTestCase {
LayoutInflater inflater = LayoutInflater.from(context);
mHolder = new DocumentHolder(getContext(), inflater.inflate(R.layout.item_doc_list, null)) {
@Override
- public void bind(Cursor cursor, String modelId) {}
+ public void bind(Cursor cursor, String modelId) {
+ }
};
mListener = new TestListener();
@@ -67,12 +68,12 @@ public class DocumentHolderTest extends AndroidTestCase {
public MotionEvent createEvent(int tooltype) {
long time = SystemClock.uptimeMillis();
- PointerProperties properties[] = new PointerProperties[] {
+ PointerProperties[] properties = new PointerProperties[]{
new PointerProperties()
};
properties[0].toolType = tooltype;
- PointerCoords coords[] = new PointerCoords[] {
+ PointerCoords[] coords = new PointerCoords[]{
new PointerCoords()
};
@@ -96,7 +97,7 @@ public class DocumentHolderTest extends AndroidTestCase {
0, // edgeflags
0, // source
0 // flags
- );
+ );
}
private class TestListener extends KeyboardEventListener<DocumentItemDetails> {
diff --git a/tests/unit/com/android/documentsui/dirlist/IconHelperTest.java b/tests/unit/com/android/documentsui/dirlist/IconHelperTest.java
index d2419e8cf..c129f0d79 100644
--- a/tests/unit/com/android/documentsui/dirlist/IconHelperTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/IconHelperTest.java
@@ -24,51 +24,120 @@ import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.documentsui.TestUserManagerState;
import com.android.documentsui.ThumbnailCache;
import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
+import com.android.documentsui.util.FeatureFlagUtils;
+import com.android.modules.utils.build.SdkLevel;
+
+import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@SmallTest
+@RunWith(Parameterized.class)
public final class IconHelperTest {
-
+ private final UserId mSystemUser = UserId.of(UserHandle.SYSTEM);
+ private final UserId mManagedUser = UserId.of(100);
+ private final UserId mPrivateUser = UserId.of(101);
private Context mContext;
private IconHelper mIconHelper;
private ThumbnailCache mThumbnailCache = new ThumbnailCache(1000);
+ private final TestUserManagerState mTestUserManagerState = new TestUserManagerState();
- private UserId systemUser = UserId.of(UserHandle.SYSTEM);
- private UserId managedUser = UserId.of(100);
+ @Parameter(0)
+ public boolean isPrivateSpaceEnabled;
+
+ /**
+ * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+ * private space flag enabled and once with it disabled.
+ */
+ @Parameters(name = "privateSpaceEnabled={0}")
+ public static Iterable<?> data() {
+ return Lists.newArrayList(true, false);
+ }
@Before
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
- mThumbnailCache, managedUser);
+ mIconHelper = FeatureFlagUtils.isPrivateSpaceEnabled()
+ ? new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
+ mThumbnailCache, null, mTestUserManagerState)
+ : new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
+ mThumbnailCache, mManagedUser, null);
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ mTestUserManagerState.userIds = SdkLevel.isAtLeastV()
+ ? Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser)
+ : Lists.newArrayList(mSystemUser, mManagedUser);
+ }
}
@Test
public void testShouldShowBadge_returnFalse_onSystemUser() {
- assertThat(mIconHelper.shouldShowBadge(systemUser.getIdentifier())).isFalse();
+ assertThat(mIconHelper.shouldShowBadge(mSystemUser.getIdentifier())).isFalse();
}
@Test
public void testShouldShowBadge_returnTrue_onManagedUser() {
- assertThat(mIconHelper.shouldShowBadge(managedUser.getIdentifier())).isTrue();
+ assertThat(mIconHelper.shouldShowBadge(mManagedUser.getIdentifier())).isTrue();
}
@Test
- public void testShouldShowBadge_returnFalse_onManagedUser_doNotShowBadge() {
+ public void testShouldShowBadge_returnTrue_onPrivateUser() {
+ if (!SdkLevel.isAtLeastV() || !FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ assertThat(mIconHelper.shouldShowBadge(mPrivateUser.getIdentifier())).isTrue();
+ }
+
+ @Test
+ public void testShouldShowBadge_returnFalseOnManagedUser_doNotShowBadge() {
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ mTestUserManagerState.userIds = Lists.newArrayList(mSystemUser, mManagedUser);
+ mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ false,
+ mThumbnailCache, null, mTestUserManagerState);
+ } else {
+ mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ false,
+ mThumbnailCache, mManagedUser, null);
+ }
+ assertThat(mIconHelper.shouldShowBadge(mManagedUser.getIdentifier())).isFalse();
+ }
+
+ @Test
+ public void testShouldShowBadge_returnFalseOnPrivateUser_doNotShowBadge() {
+ if (!FeatureFlagUtils.isPrivateSpaceEnabled()) return;
mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ false,
- mThumbnailCache, managedUser);
- assertThat(mIconHelper.shouldShowBadge(managedUser.getIdentifier())).isFalse();
+ mThumbnailCache, null, mTestUserManagerState);
+ assertThat(mIconHelper.shouldShowBadge(mPrivateUser.getIdentifier())).isFalse();
+ }
+
+ @Test
+ public void testShouldShowBadge_returnFalseOnManagedUser_withoutManagedUser() {
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
+ mThumbnailCache, /* mManagedUser= */ null, null);
+ assertThat(mIconHelper.shouldShowBadge(mManagedUser.getIdentifier())).isFalse();
+ }
+
+ @Test
+ public void testShouldShowBadge_returnFalseOnManagedUser_withoutMultipleUsers() {
+ if (!FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ mTestUserManagerState.userIds = Lists.newArrayList(mManagedUser);
+ mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
+ mThumbnailCache, /* mManagedUser= */ null, mTestUserManagerState);
+ assertThat(mIconHelper.shouldShowBadge(mManagedUser.getIdentifier())).isFalse();
}
@Test
- public void testShouldShowBadge_returnFalse_onManagedUser_withoutManagedUser() {
+ public void testShouldShowBadge_returnFalseOnPrivateUser_withoutMultipleUsers() {
+ if (!SdkLevel.isAtLeastV() || !FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+ mTestUserManagerState.userIds = Lists.newArrayList(mPrivateUser);
mIconHelper = new IconHelper(mContext, State.MODE_LIST, /* maybeShowBadge= */ true,
- mThumbnailCache, /* managedUser= */ null);
- assertThat(mIconHelper.shouldShowBadge(managedUser.getIdentifier())).isFalse();
+ mThumbnailCache, /* mManagedUser= */ null, mTestUserManagerState);
+ assertThat(mIconHelper.shouldShowBadge(mPrivateUser.getIdentifier())).isFalse();
}
}
diff --git a/tests/unit/com/android/documentsui/dirlist/InflateMessageDocumentHolderTest.java b/tests/unit/com/android/documentsui/dirlist/InflateMessageDocumentHolderTest.java
index 436e4fbca..e929f1152 100644
--- a/tests/unit/com/android/documentsui/dirlist/InflateMessageDocumentHolderTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/InflateMessageDocumentHolderTest.java
@@ -18,7 +18,11 @@ package com.android.documentsui.dirlist;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
+import android.content.pm.UserProperties;
+import android.os.UserManager;
import android.view.View;
import android.widget.Button;
@@ -29,14 +33,28 @@ import com.android.documentsui.CrossProfileQuietModeException;
import com.android.documentsui.Model;
import com.android.documentsui.R;
import com.android.documentsui.base.State;
+import com.android.documentsui.base.UserId;
import com.android.documentsui.testing.TestActionHandler;
import com.android.documentsui.testing.TestEnv;
import com.android.documentsui.testing.TestProvidersAccess;
+import com.android.documentsui.testing.UserManagers;
+import com.android.documentsui.util.FeatureFlagUtils;
+import com.android.modules.utils.build.SdkLevel;
+
+import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.HashMap;
+import java.util.Map;
@SmallTest
+@RunWith(Parameterized.class)
public final class InflateMessageDocumentHolderTest {
private Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -46,22 +64,65 @@ public final class InflateMessageDocumentHolderTest {
private TestActionHandler mTestActionHandler = new TestActionHandler();
private InflateMessageDocumentHolder mHolder;
+ @Parameter(0)
+ public boolean isPrivateSpaceEnabled;
+
+ /**
+ * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+ * private space flag enabled and once with it disabled.
+ */
+ @Parameters(name = "privateSpaceEnabled={0}")
+ public static Iterable<?> data() {
+ return Lists.newArrayList(true, false);
+ }
+
@Before
public void setUp() {
DocumentsAdapter.Environment env =
new TestEnvironment(mContext, TestEnv.create(), mTestActionHandler);
env.getDisplayState().action = State.ACTION_GET_CONTENT;
- env.getDisplayState().canShareAcrossProfile = true;
env.getDisplayState().supportsCrossProfile = true;
- mInflateMessage = new Message.InflateMessage(env, mDefaultCallback);
+
mContext.setTheme(R.style.DocumentsTheme);
mContext.getTheme().applyStyle(R.style.DocumentsDefaultTheme, /* force= */false);
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ Map<UserId, String> userIdToLabelMap = new HashMap<>();
+ userIdToLabelMap.put(TestProvidersAccess.USER_ID, "Personal");
+ userIdToLabelMap.put(TestProvidersAccess.OtherUser.USER_ID, "Work");
+ env.getDisplayState().canForwardToProfileIdMap.put(TestProvidersAccess.USER_ID, true);
+ env.getDisplayState().canForwardToProfileIdMap.put(
+ TestProvidersAccess.OtherUser.USER_ID, true);
+ UserManager userManager = UserManagers.create();
+ if (SdkLevel.isAtLeastV()) {
+ userIdToLabelMap.put(TestProvidersAccess.AnotherUser.USER_ID, "Private");
+ env.getDisplayState().canForwardToProfileIdMap.put(
+ TestProvidersAccess.AnotherUser.USER_ID, true);
+ UserProperties otherUserProperties = new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ UserProperties anotherUserProperties = new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ when(userManager.getUserProperties(TestProvidersAccess.OtherUser.USER_HANDLE))
+ .thenReturn(otherUserProperties);
+ when(userManager.getUserProperties(TestProvidersAccess.AnotherUser.USER_HANDLE))
+ .thenReturn(anotherUserProperties);
+ }
+
+ mInflateMessage = new Message.InflateMessage(env, mDefaultCallback,
+ TestProvidersAccess.USER_ID,
+ TestProvidersAccess.OtherUser.USER_ID, userIdToLabelMap, userManager);
+ } else {
+ mInflateMessage = new Message.InflateMessage(env, mDefaultCallback);
+ env.getDisplayState().canShareAcrossProfile = true;
+ }
mHolder = new InflateMessageDocumentHolder(mContext, /* parent= */null);
}
@Test
public void testClickingButtonShouldShowProgressBar() {
+ if (SdkLevel.isAtLeastV()) return;
Model.Update error = new Model.Update(
new CrossProfileQuietModeException(TestProvidersAccess.OtherUser.USER_ID),
/* remoteActionsEnabled= */ true);
diff --git a/tests/unit/com/android/documentsui/dirlist/MessageTest.java b/tests/unit/com/android/documentsui/dirlist/MessageTest.java
index 0816684d7..93266abcd 100644
--- a/tests/unit/com/android/documentsui/dirlist/MessageTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/MessageTest.java
@@ -16,6 +16,8 @@
package com.android.documentsui.dirlist;
+import static com.android.documentsui.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static com.android.documentsui.DevicePolicyResources.Drawables.WORK_PROFILE_OFF_ICON;
import static com.android.documentsui.DevicePolicyResources.Strings.CANT_SELECT_WORK_FILES_MESSAGE;
import static com.android.documentsui.DevicePolicyResources.Strings.CANT_SELECT_WORK_FILES_TITLE;
import static com.android.documentsui.DevicePolicyResources.Strings.WORK_PROFILE_OFF_ENABLE_BUTTON;
@@ -31,6 +33,9 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
+import android.content.pm.UserProperties;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
import android.os.UserManager;
import androidx.core.util.Preconditions;
@@ -45,12 +50,17 @@ import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
import com.android.documentsui.testing.TestActionHandler;
import com.android.documentsui.testing.TestEnv;
+import com.android.documentsui.testing.TestProvidersAccess;
import com.android.documentsui.testing.UserManagers;
+import com.android.documentsui.util.FeatureFlagUtils;
import com.android.modules.utils.build.SdkLevel;
import org.junit.Before;
import org.junit.Test;
+import java.util.HashMap;
+import java.util.Map;
+
@SmallTest
public final class MessageTest {
@@ -69,7 +79,8 @@ public final class MessageTest {
mUserManager = UserManagers.create();
mTestActionHandler = new TestActionHandler();
mDevicePolicyManager = mock(DevicePolicyManager.class);
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemServiceName(UserManager.class)).thenReturn("mUserManager");
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mContext.getSystemServiceName(DevicePolicyManager.class))
.thenReturn(Context.DEVICE_POLICY_SERVICE);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
@@ -79,7 +90,24 @@ public final class MessageTest {
DocumentsAdapter.Environment env =
new TestEnvironment(mContext, TestEnv.create(), mTestActionHandler);
env.getDisplayState().action = State.ACTION_GET_CONTENT;
- mInflateMessage = new Message.InflateMessage(env, mDefaultCallback);
+ if (SdkLevel.isAtLeastV()) {
+ UserProperties userProperties = new UserProperties.Builder()
+ .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .build();
+ UserHandle userHandle = UserHandle.of(mUserId.getIdentifier());
+ when(mUserManager.getUserProperties(userHandle)).thenReturn(userProperties);
+ }
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ String personalLabel = mContext.getString(R.string.personal_tab);
+ String workLabel = mContext.getString(R.string.work_tab);
+ Map<UserId, String> userIdToLabelMap = new HashMap<>();
+ userIdToLabelMap.put(TestProvidersAccess.USER_ID, personalLabel);
+ userIdToLabelMap.put(mUserId, workLabel);
+ mInflateMessage = new Message.InflateMessage(env, mDefaultCallback,
+ TestProvidersAccess.USER_ID, mUserId, userIdToLabelMap, mUserManager);
+ } else {
+ mInflateMessage = new Message.InflateMessage(env, mDefaultCallback);
+ }
}
@Test
@@ -115,6 +143,7 @@ public final class MessageTest {
@Test
public void testInflateMessage_updateToCrossProfileQuietMode() {
+ if (SdkLevel.isAtLeastV()) return;
Model.Update error = new Model.Update(
new CrossProfileQuietModeException(mUserId),
/* isRemoteActionsEnabled= */ true);
@@ -143,4 +172,47 @@ public final class MessageTest {
assertThat(mTestActionHandler.mRequestDisablingQuietModeHappened).isTrue();
}
+
+ @Test
+ public void testInflateMessage_updateToCrossProfileQuietMode_PostV() {
+ if (!SdkLevel.isAtLeastV()) return;
+ Model.Update error = new Model.Update(
+ new CrossProfileQuietModeException(mUserId),
+ /* isRemoteActionsEnabled= */ true);
+
+ DevicePolicyResourcesManager devicePolicyResourcesManager = mock(
+ DevicePolicyResourcesManager.class);
+ when(mDevicePolicyManager.getResources()).thenReturn(devicePolicyResourcesManager);
+
+ if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ Drawable icon = mContext.getDrawable(R.drawable.work_off);
+ when(devicePolicyResourcesManager.getDrawable(eq(WORK_PROFILE_OFF_ICON), eq(OUTLINE),
+ any()))
+ .thenReturn(icon);
+ } else {
+ String title = mContext.getString(R.string.quiet_mode_error_title);
+ String text = mContext.getString(R.string.quiet_mode_button);
+ when(devicePolicyResourcesManager.getString(eq(WORK_PROFILE_OFF_ERROR_TITLE), any()))
+ .thenReturn(title);
+ when(devicePolicyResourcesManager.getString(eq(WORK_PROFILE_OFF_ENABLE_BUTTON), any()))
+ .thenReturn(text);
+ }
+ mInflateMessage.update(error);
+
+ assertThat(mInflateMessage.getLayout())
+ .isEqualTo(InflateMessageDocumentHolder.LAYOUT_CROSS_PROFILE_ERROR);
+
+ if (!FeatureFlagUtils.isPrivateSpaceEnabled()) {
+ assert mInflateMessage.getTitleString() != null;
+ assertThat(mInflateMessage.getTitleString().toString())
+ .isEqualTo(mContext.getString(R.string.quiet_mode_error_title));
+ assert mInflateMessage.getButtonString() != null;
+ assertThat(mInflateMessage.getButtonString().toString()).isEqualTo(
+ mContext.getString(R.string.quiet_mode_button));
+ }
+ assertThat(mInflateMessage.mCallback).isNotNull();
+ mInflateMessage.mCallback.run();
+
+ assertThat(mTestActionHandler.mRequestDisablingQuietModeHappened).isTrue();
+ }
}