summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();
+ }
}