Merge "Fix DocsUI cross-profile error screens in landscape" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 7a70bd6..0460570 100644
--- a/Android.bp
+++ b/Android.bp
@@ -45,7 +45,6 @@
min_sdk_version: "29",
plugins: [
- "compat-changeid-annotation-processor",
"java_api_finder",
],
}
diff --git a/res/layout/directory_header.xml b/res/layout/directory_header.xml
index 45abeb8..4c4d845 100644
--- a/res/layout/directory_header.xml
+++ b/res/layout/directory_header.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/action_bar_space_margin"
@@ -28,11 +29,33 @@
<!-- used for search chip. -->
<include layout="@layout/search_chip_row"/>
- <com.google.android.material.tabs.TabLayout
- android:id="@+id/tabs"
+ <LinearLayout
+ android:id="@+id/tabs_container"
+ android:theme="@style/TabTheme"
+ android:clipToPadding="true"
+ android:clipChildren="true"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.google.android.material.tabs.TabLayout
+ android:id="@+id/tabs"
+ android:background="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:tabMaxWidth="0dp"
+ app:tabGravity="fill"
+ app:tabMode="fixed"
+ app:tabIndicatorColor="@color/tab_indicator_color"
+ app:tabSelectedTextColor="@color/tab_indicator_color"
+ app:tabTextAppearance="@style/TabTextAppearance"
+ app:tabTextColor="?android:attr/textColorSecondary"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+ </LinearLayout>
<!-- used for apps row. -->
<include layout="@layout/apps_row"/>
diff --git a/res/layout/drawer_layout.xml b/res/layout/drawer_layout.xml
index bf5bfb4..a5331d5 100644
--- a/res/layout/drawer_layout.xml
+++ b/res/layout/drawer_layout.xml
@@ -28,14 +28,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <FrameLayout
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/scrolling_behavior">
<LinearLayout
android:layout_width="match_parent"
@@ -70,7 +71,7 @@
<include layout="@layout/directory_app_bar"/>
- </FrameLayout>
+ </androidx.coordinatorlayout.widget.CoordinatorLayout>
<LinearLayout
android:id="@+id/drawer_roots"
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index f358b02..1a6c174 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -21,4 +21,12 @@
<color name="primary">#8AB4F8</color>
<color name="secondary">#3D8AB4F8</color>
<color name="hairline">#5F6368</color>
+
+ <color name="briefcase_icon_color">#669DF6</color> <!-- Blue 400 -->
+ <color name="cross_profile_button_text_color">#669DF6</color> <!-- Blue 400 -->
+ <color name="empty_state_text">@android:color/white</color>
+ <color name="error_image_color">@android:color/white</color>
+
+ <color name="edge_effect">@android:color/white</color>
+ <color name="tab_indicator_color">#669DF6</color> <!-- Blue 400 -->
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index fb00e16..9016664 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -46,4 +46,7 @@
<color name="cross_profile_button_text_color">#1A73E8</color>
<color name="empty_state_text">#202124</color>
<color name="error_image_color">#757575</color>
+
+ <color name="tab_indicator_color">#1A73E8</color> <!-- Blue 600 -->
+ <color name="edge_effect">@android:color/black</color>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ee71ac7..bac7887 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -514,7 +514,7 @@
<string name="open_tree_dialog_message">This will let <xliff:g id="appName" example="Drive">%1$s</xliff:g> access current and future content stored in <xliff:g id="directory" example="DCIM">%2$s</xliff:g>.</string>
<!-- Header message title show on open document tree flow when directory is blocked. [CHAR_LIMIT=48] -->
<string name="directory_blocked_header_title">Choose another folder</string>
- <!-- Header message subtitle show on open document tree flow when directory is blocked. [CHAR_LIMIT=80]-->
+ <!-- Header message subtitle show on open document tree flow when directory is blocked. [CHAR_LIMIT=90]-->
<string name="directory_blocked_header_subtitle">This folder can\u2019t be used for security reasons</string>
<!-- Button text for the "create new folder" button. [CHAR_LIMIT=48] -->
<string name="create_new_folder_button">Create new folder</string>
diff --git a/res/values/styles_text.xml b/res/values/styles_text.xml
index 592df8e..3ea510c 100644
--- a/res/values/styles_text.xml
+++ b/res/values/styles_text.xml
@@ -114,4 +114,9 @@
<item name="fontFamily">@string/config_fontFamilyMedium</item>
</style>
+ <style name="TabTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textSize">14sp</item>
+ <item name="fontFamily">@string/config_fontFamilyMedium</item>
+ </style>
+
</resources>
\ No newline at end of file
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 971fa1f..a1029be 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -72,4 +72,8 @@
<item name="queryBackground">@color/menu_search_background</item>
<item name="snackbarButtonStyle">@style/SnackbarButtonStyle</item>
</style>
+
+ <style name="TabTheme" parent="@style/Theme.MaterialComponents.DayNight">
+ <item name="colorPrimary">@color/edge_effect</item>
+ </style>
</resources>
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index c694c0c..54674e1 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -162,11 +162,11 @@
Breadcrumb breadcrumb = findViewById(R.id.horizontal_breadcrumb);
assert(breadcrumb != null);
- TabLayout profileTabs = findViewById(R.id.tabs);
- assert (profileTabs != null);
+ View profileTabsContainer = findViewById(R.id.tabs_container);
+ assert (profileTabsContainer != null);
mNavigator = new NavigationViewManager(this, mDrawer, mState, this, breadcrumb,
- profileTabs, DocumentsApplication.getUserIdManager(this));
+ profileTabsContainer, DocumentsApplication.getUserIdManager(this));
SearchManagerListener searchListener = new SearchManagerListener() {
/**
* Called when search results changed. Refreshes the content of the directory. It
diff --git a/src/com/android/documentsui/HorizontalBreadcrumb.java b/src/com/android/documentsui/HorizontalBreadcrumb.java
index d5987e1..5cfc65e 100644
--- a/src/com/android/documentsui/HorizontalBreadcrumb.java
+++ b/src/com/android/documentsui/HorizontalBreadcrumb.java
@@ -30,7 +30,6 @@
import com.android.documentsui.NavigationViewManager.Breadcrumb;
import com.android.documentsui.NavigationViewManager.Environment;
-import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.dirlist.AccessibilityEventRouter;
import java.util.function.Consumer;
@@ -65,7 +64,7 @@
IntConsumer listener) {
mClickListener = listener;
- mLayoutManager = new LinearLayoutManager(
+ mLayoutManager = new HorizontalBreadcrumbLinearLayoutManager(
getContext(), LinearLayoutManager.HORIZONTAL, false);
mAdapter = new BreadcrumbAdapter(state, env, this::onKey);
// Since we are using GestureDetector to detect click events, a11y services don't know which
@@ -163,7 +162,7 @@
mState = state;
mEnv = env;
mClickListener = clickListener;
- mLastItemSize = mState.stack.size();
+ mLastItemSize = getItemCount();
}
@Override
@@ -175,14 +174,16 @@
@Override
public void onBindViewHolder(BreadcrumbHolder holder, int position) {
- final DocumentInfo doc = getItem(position);
final int padding = (int) holder.itemView.getResources()
.getDimension(R.dimen.breadcrumb_item_padding);
final int enableColor = holder.itemView.getContext().getColor(R.color.primary);
final boolean isFirst = position == 0;
+ // Note that when isFirst is true, there might not be a DocumentInfo on the stack as it
+ // could be an error state screen accessible from the root info.
final boolean isLast = position == getItemCount() - 1;
- holder.mTitle.setText(isFirst ? mEnv.getCurrentRoot().title : doc.displayName);
+ holder.mTitle.setText(
+ isFirst ? mEnv.getCurrentRoot().title : mState.stack.get(position).displayName);
holder.mTitle.setTextColor(isLast ? enableColor : holder.mDefaultTextColor);
holder.mTitle.setPadding(isFirst ? padding * 3 : padding,
padding, isLast ? padding * 2 : padding, padding);
@@ -192,12 +193,19 @@
holder.setLast(isLast);
}
- private DocumentInfo getItem(int position) {
- return mState.stack.get(position);
- }
-
@Override
public int getItemCount() {
+ // Don't show recents in the breadcrumb.
+ if (mState.stack.isRecents()) {
+ return 0;
+ }
+ // Continue showing the root title in the breadcrumb for cross-profile error screens.
+ if (mState.supportsCrossProfile()
+ && mState.stack.size() == 0
+ && mState.stack.getRoot() != null
+ && mState.stack.getRoot().supportsCrossProfile()) {
+ return 1;
+ }
return mState.stack.size();
}
@@ -206,7 +214,7 @@
}
public void updateLastItemSize() {
- mLastItemSize = mState.stack.size();
+ mLastItemSize = getItemCount();
}
}
@@ -238,4 +246,22 @@
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
+
+ private static class HorizontalBreadcrumbLinearLayoutManager extends LinearLayoutManager {
+
+ /**
+ * Disable predictive animations. There is a bug in RecyclerView which causes views that
+ * are being reloaded to pull invalid view holders from the internal recycler stack if the
+ * adapter size has decreased since the ViewHolder was recycled.
+ */
+ @Override
+ public boolean supportsPredictiveItemAnimations() {
+ return false;
+ }
+
+ HorizontalBreadcrumbLinearLayoutManager(
+ Context context, int orientation, boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ }
+ }
}
diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java
index 00591d7..204971d 100644
--- a/src/com/android/documentsui/NavigationViewManager.java
+++ b/src/com/android/documentsui/NavigationViewManager.java
@@ -64,7 +64,7 @@
State state,
NavigationViewManager.Environment env,
Breadcrumb breadcrumb,
- TabLayout tabLayout,
+ View tabLayoutContainer,
UserIdManager userIdManager) {
mToolbar = activity.findViewById(R.id.toolbar);
@@ -73,7 +73,7 @@
mEnv = env;
mBreadcrumb = breadcrumb;
mBreadcrumb.setup(env, state, this::onNavigationItemSelected);
- mProfileTabs = new ProfileTabs(tabLayout, mState, userIdManager, mEnv, activity);
+ mProfileTabs = new ProfileTabs(tabLayoutContainer, mState, userIdManager, mEnv, activity);
mToolbar.setNavigationOnClickListener(
new View.OnClickListener() {
diff --git a/src/com/android/documentsui/ProfileTabs.java b/src/com/android/documentsui/ProfileTabs.java
index 0e30ea6..7149de6 100644
--- a/src/com/android/documentsui/ProfileTabs.java
+++ b/src/com/android/documentsui/ProfileTabs.java
@@ -39,6 +39,7 @@
public class ProfileTabs implements ProfileTabsAddons {
private static final float DISABLED_TAB_OPACITY = 0.38f;
+ private final View mTabsContainer;
private final TabLayout mTabs;
private final State mState;
private final NavigationViewManager.Environment mEnv;
@@ -49,10 +50,11 @@
private Listener mListener;
private TabLayout.OnTabSelectedListener mOnTabSelectedListener;
- public ProfileTabs(TabLayout tabLayout, State state, UserIdManager userIdManager,
+ public ProfileTabs(View tabLayoutContainer, State state, UserIdManager userIdManager,
NavigationViewManager.Environment env,
AbstractActionHandler.CommonAddons commonAddons) {
- mTabs = checkNotNull(tabLayout);
+ mTabsContainer = checkNotNull(tabLayoutContainer);
+ mTabs = tabLayoutContainer.findViewById(R.id.tabs);
mState = checkNotNull(state);
mEnv = checkNotNull(env);
mCommonAddons = checkNotNull(commonAddons);
@@ -93,7 +95,7 @@
mTabs.selectTab(mTabs.getTabAt(mUserIds.indexOf(currentRoot.userId)));
mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
}
- mTabs.setVisibility(shouldShow() ? View.VISIBLE : View.GONE);
+ mTabsContainer.setVisibility(shouldShow() ? View.VISIBLE : View.GONE);
}
public void setListener(@Nullable Listener listener) {
diff --git a/src/com/android/documentsui/picker/PickFragment.java b/src/com/android/documentsui/picker/PickFragment.java
index f7b98e3..bd25f00 100644
--- a/src/com/android/documentsui/picker/PickFragment.java
+++ b/src/com/android/documentsui/picker/PickFragment.java
@@ -51,6 +51,7 @@
private static final String ACTION_KEY = "action";
private static final String COPY_OPERATION_SUBTYPE_KEY = "copyOperationSubType";
private static final String PICK_TARGET_KEY = "pickTarget";
+ private static final String RESTRICT_SCOPE_STORAGE_KEY = "restrictScopeStorage";
private final View.OnClickListener mPickListener = new View.OnClickListener() {
@Override
@@ -120,6 +121,7 @@
mCopyOperationSubType =
savedInstanceState.getInt(COPY_OPERATION_SUBTYPE_KEY);
mPickTarget = savedInstanceState.getParcelable(PICK_TARGET_KEY);
+ mRestrictScopeStorage = savedInstanceState.getBoolean(RESTRICT_SCOPE_STORAGE_KEY);
updateView();
}
@@ -132,6 +134,7 @@
outState.putInt(ACTION_KEY, mAction);
outState.putInt(COPY_OPERATION_SUBTYPE_KEY, mCopyOperationSubType);
outState.putParcelable(PICK_TARGET_KEY, mPickTarget);
+ outState.putBoolean(RESTRICT_SCOPE_STORAGE_KEY, mRestrictScopeStorage);
}
/**
diff --git a/tests/common/com/android/documentsui/bots/DirectoryListBot.java b/tests/common/com/android/documentsui/bots/DirectoryListBot.java
index 08d2995..9fbc39f 100644
--- a/tests/common/com/android/documentsui/bots/DirectoryListBot.java
+++ b/tests/common/com/android/documentsui/bots/DirectoryListBot.java
@@ -164,7 +164,7 @@
private UiObject findHeaderMessageButton() {
return findObject(
mDirContainerId,
- mTargetPackage + ":id/button_dismiss");
+ mTargetPackage + ":id/dismiss_button");
}
private UiObject findPlaceholderMessageTextView() {