Merge "Ensure thumbnail cache test suite is running on main thread."
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 86bbd075..a068095 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2512,7 +2512,6 @@
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionGroup);
-
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission-group>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
@@ -3361,7 +3360,9 @@
}
}
- int roundIconVal = sa.getResourceId(roundIconRes, 0);
+ final boolean useRoundIcon =
+ Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ int roundIconVal = useRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
if (roundIconVal != 0) {
outInfo.icon = roundIconVal;
outInfo.nonLocalizedLabel = null;
@@ -4575,10 +4576,12 @@
outInfo.nonLocalizedLabel = v.coerceToString();
}
- int roundIcon = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
- if (roundIcon != 0) {
- outInfo.icon = roundIcon;
+ final boolean useRoundIcon =
+ Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ int roundIconVal = useRoundIcon ? sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
+ if (roundIconVal != 0) {
+ outInfo.icon = roundIconVal;
} else {
outInfo.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 28281b3c..8addffb0 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -370,6 +370,8 @@
systemInsets.bottom);
final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
systemInsets.right);
+ final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
+ systemInsets.left);
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -379,9 +381,11 @@
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- final int size = DecorView.getNavBarSize(bottomInset, rightInset);
+ final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
mNavigationBarColor.setBounds(width - size, 0, width, height);
+ } else if (DecorView.isNavBarToLeftEdge(bottomInset, rightInset)) {
+ mNavigationBarColor.setBounds(0, 0, size, height);
} else {
mNavigationBarColor.setBounds(0, height - size, width, height);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 7e38d9b..1099ef7 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -97,7 +97,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -162,13 +161,13 @@
private final ColorViewState mStatusColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
- Gravity.TOP, Gravity.LEFT,
+ Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.statusBarBackground,
FLAG_FULLSCREEN);
private final ColorViewState mNavigationColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
- Gravity.BOTTOM, Gravity.RIGHT,
+ Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.navigationBarBackground,
0 /* hideWindowFlag */);
@@ -184,9 +183,11 @@
private int mLastTopInset = 0;
private int mLastBottomInset = 0;
private int mLastRightInset = 0;
+ private int mLastLeftInset = 0;
private boolean mLastHasTopStableInset = false;
private boolean mLastHasBottomStableInset = false;
private boolean mLastHasRightStableInset = false;
+ private boolean mLastHasLeftStableInset = false;
private int mLastWindowFlags = 0;
private boolean mLastShouldAlwaysConsumeNavBar = false;
@@ -991,12 +992,21 @@
return Math.min(stableRight, systemRight);
}
+ static int getColorViewLeftInset(int stableLeft, int systemLeft) {
+ return Math.min(stableLeft, systemLeft);
+ }
+
static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
- static int getNavBarSize(int bottomInset, int rightInset) {
- return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset;
+ static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
+ return bottomInset == 0 && leftInset > 0;
+ }
+
+ static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
+ return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
+ : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
@@ -1016,6 +1026,8 @@
insets.getSystemWindowInsetBottom());
mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
insets.getSystemWindowInsetRight());
+ mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
+ insets.getSystemWindowInsetLeft());
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
@@ -1031,21 +1043,32 @@
boolean hasRightStableInset = insets.getStableInsetRight() != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
+
+ boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
+ disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
+ mLastHasLeftStableInset = hasLeftStableInset;
+
mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
}
boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
- int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset);
+ boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
+ int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
- mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
- 0 /* rightInset */, animate && !disallowAnimate, false /* force */);
+ mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge,
+ navBarToLeftEdge,
+ 0 /* sideInset */, animate && !disallowAnimate, false /* force */);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
- int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
+ boolean statusBarNeedsLeftInset = navBarToLeftEdge
+ && mNavigationColorViewState.present;
+ int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
+ : statusBarNeedsLeftInset ? mLastLeftInset : 0;
updateColorViewInt(mStatusColorViewState, sysUiVisibility,
calculateStatusBarColor(), mLastTopInset,
- false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate,
+ false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
+ animate && !disallowAnimate,
mForceWindowDrawsStatusBarBackground);
}
@@ -1070,15 +1093,17 @@
int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
+ int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
if (mContentRoot != null
&& mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
- || lp.bottomMargin != consumedBottom) {
+ || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
lp.topMargin = consumedTop;
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
+ lp.leftMargin = consumedLeft;
mContentRoot.setLayoutParams(lp);
if (insets == null) {
@@ -1089,7 +1114,7 @@
}
if (insets != null) {
insets = insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetLeft() - consumedLeft,
insets.getSystemWindowInsetTop() - consumedTop,
insets.getSystemWindowInsetRight() - consumedRight,
insets.getSystemWindowInsetBottom() - consumedBottom);
@@ -1126,11 +1151,12 @@
* @param size the current size in the non-parent-matching dimension.
* @param verticalBar if true the view is attached to a vertical edge, otherwise to a
* horizontal edge,
- * @param rightMargin rightMargin for the color view.
+ * @param sideMargin sideMargin for the color view.
* @param animate if true, the change will be animated.
*/
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
- int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
+ int size, boolean verticalBar, boolean seascape, int sideMargin,
+ boolean animate, boolean force) {
state.present = (sysUiVis & state.systemUiHideFlag) == 0
&& (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
&& ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
@@ -1145,7 +1171,9 @@
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
- int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
+ int resolvedGravity = verticalBar
+ ? (seascape ? state.seascapeGravity : state.horizontalGravity)
+ : state.verticalGravity;
if (view == null) {
if (showView) {
@@ -1159,7 +1187,11 @@
LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
resolvedGravity);
- lp.rightMargin = rightMargin;
+ if (seascape) {
+ lp.leftMargin = sideMargin;
+ } else {
+ lp.rightMargin = sideMargin;
+ }
addView(view, lp);
updateColorViewTranslations();
}
@@ -1168,12 +1200,16 @@
visibilityChanged = state.targetVisibility != vis;
state.targetVisibility = vis;
LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ int rightMargin = seascape ? 0 : sideMargin;
+ int leftMargin = seascape ? sideMargin : 0;
if (lp.height != resolvedHeight || lp.width != resolvedWidth
- || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+ || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
+ || lp.leftMargin != leftMargin) {
lp.height = resolvedHeight;
lp.width = resolvedWidth;
lp.gravity = resolvedGravity;
lp.rightMargin = rightMargin;
+ lp.leftMargin = leftMargin;
view.setLayoutParams(lp);
}
if (showView) {
@@ -2210,17 +2246,19 @@
final int translucentFlag;
final int verticalGravity;
final int horizontalGravity;
+ final int seascapeGravity;
final String transitionName;
final int hideWindowFlag;
ColorViewState(int systemUiHideFlag,
int translucentFlag, int verticalGravity, int horizontalGravity,
- String transitionName, int id, int hideWindowFlag) {
+ int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
this.id = id;
this.systemUiHideFlag = systemUiHideFlag;
this.translucentFlag = translucentFlag;
this.verticalGravity = verticalGravity;
this.horizontalGravity = horizontalGravity;
+ this.seascapeGravity = seascapeGravity;
this.transitionName = transitionName;
this.hideWindowFlag = hideWindowFlag;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4f6915c..5a46bfc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2492,4 +2492,7 @@
<!-- Component that is the default launcher when demo mode is enabled. -->
<string name="config_demoModeLauncherComponent"></string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1009294..1f6e8cf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2615,4 +2615,6 @@
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
+
+ <java-symbol type="bool" name="config_useRoundIcon" />
</resources>
diff --git a/docs/html/training/testing/performance.jd b/docs/html/training/testing/performance.jd
index 8592c0f..0c0ab7f 100644
--- a/docs/html/training/testing/performance.jd
+++ b/docs/html/training/testing/performance.jd
@@ -437,15 +437,25 @@
</p>
<ul>
- <li>Rendering Performance 101
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=HXQhu6qfTVU">
+ Rendering Performance 101</a>
</li>
- <li>Why 60fps?
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=CaMTIgxCSqU">
+ Why 60fps?</a>
</li>
- <li>Android UI and the GPU
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=WH9AFhgwmDw">
+ Android, UI, and the GPU</a>
</li>
- <li>Invalidations Layouts and performance
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=we6poP0kw6E">
+ Invalidations, Layouts, and Performance</a>
</li>
- <li>Analyzing UI Performance with Systrace
+ <li>
+ <a href="{@docRoot}studio/profile/systrace.html">
+ Analyzing UI Performance with Systrace</a>
</li>
</ul>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index efb83f2..e13e982 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -54,6 +54,7 @@
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
+import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
@@ -95,6 +96,8 @@
public abstract void onDocumentPicked(DocumentInfo doc, Model model);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
+ public abstract FragmentTuner createFragmentTuner();
+ public abstract MenuManager getMenuManager();
abstract void onTaskFinished(Uri... uris);
abstract void refreshDirectory(int anim);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 2ca13dc..830b118 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -43,10 +43,13 @@
import android.view.Menu;
import android.view.MenuItem;
+import com.android.documentsui.MenuManager.DirectoryDetails;
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
+import com.android.documentsui.dirlist.FragmentTuner;
+import com.android.documentsui.dirlist.FragmentTuner.DocumentsTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DurableUtils;
@@ -64,6 +67,8 @@
public class DocumentsActivity extends BaseActivity {
private static final int CODE_FORWARD = 42;
private static final String TAG = "DocumentsActivity";
+ private DocumentsMenuManager mMenuManager;
+ private DirectoryDetails mDetails;
public DocumentsActivity() {
super(R.layout.documents_activity, TAG);
@@ -72,6 +77,8 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mMenuManager = new DocumentsMenuManager(mSearchManager, getDisplayState());
+ mDetails = new DirectoryDetails(this);
if (mState.action == ACTION_CREATE) {
final String mimeType = getIntent().getType();
@@ -214,44 +221,15 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ mMenuManager.updateOptionMenu(menu, mDetails);
final DocumentInfo cwd = getCurrentDirectory();
- boolean picking = mState.action == ACTION_CREATE
- || mState.action == ACTION_OPEN_TREE
- || mState.action == ACTION_PICK_COPY_DESTINATION;
-
- if (picking) {
- // May already be hidden because the root
- // doesn't support search.
- mSearchManager.showMenu(false);
- }
-
- final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem grid = menu.findItem(R.id.menu_grid);
- final MenuItem list = menu.findItem(R.id.menu_list);
- final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
-
-
- createDir.setVisible(picking);
- createDir.setEnabled(canCreateDirectory());
-
- // No display options in recent directories
- boolean inRecents = cwd == null;
- if (picking && inRecents) {
- grid.setVisible(false);
- list.setVisible(false);
- }
-
- fileSize.setVisible(fileSize.isVisible() && !picking);
-
if (mState.action == ACTION_CREATE) {
final FragmentManager fm = getFragmentManager();
SaveFragment.get(fm).prepareForDirectory(cwd);
}
- Menus.disableHiddenItems(menu);
-
return true;
}
@@ -424,6 +402,18 @@
return (DocumentsActivity) fragment.getActivity();
}
+ @Override
+ public FragmentTuner createFragmentTuner() {
+ // Currently DocumentsTuner maintains a state specific to the fragment instance. Because of
+ // that, we create a new instance everytime it is needed
+ return new DocumentsTuner(this, getDisplayState());
+ }
+
+ @Override
+ public MenuManager getMenuManager() {
+ return mMenuManager;
+ }
+
/**
* Loads the last used path (stack) from Recents (history).
* The path selected is based on the calling package name. So the last
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsMenuManager.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsMenuManager.java
new file mode 100644
index 0000000..e7f0e74
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsMenuManager.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
+import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
+
+import android.view.Menu;
+import android.view.MenuItem;
+
+final class DocumentsMenuManager implements MenuManager {
+
+ private final State mState;
+ private final SearchViewManager mSearchManager;
+
+ public DocumentsMenuManager(SearchViewManager searchManager, State displayState) {
+ mSearchManager = searchManager;
+ mState = displayState;
+ }
+
+ @Override
+ public void updateActionMenu(Menu menu, MenuManager.SelectionDetails selection) {
+ MenuItem open = menu.findItem(R.id.menu_open);
+ MenuItem share = menu.findItem(R.id.menu_share);
+ MenuItem delete = menu.findItem(R.id.menu_delete);
+ MenuItem rename = menu.findItem(R.id.menu_rename);
+ MenuItem selectAll = menu.findItem(R.id.menu_select_all);
+
+ open.setVisible(mState.action == ACTION_GET_CONTENT
+ || mState.action == ACTION_OPEN);
+ share.setVisible(false);
+ delete.setVisible(false);
+ rename.setVisible(false);
+ selectAll.setVisible(mState.allowMultiple);
+
+ Menus.disableHiddenItems(menu);
+ }
+
+ @Override
+ public void updateOptionMenu(Menu menu, DirectoryDetails details) {
+
+ boolean picking = mState.action == ACTION_CREATE
+ || mState.action == ACTION_OPEN_TREE
+ || mState.action == ACTION_PICK_COPY_DESTINATION;
+
+ if (picking) {
+ // May already be hidden because the root
+ // doesn't support search.
+ mSearchManager.showMenu(false);
+ }
+
+ final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+ final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+
+ createDir.setVisible(picking);
+ createDir.setEnabled(details.canCreateDirectory());
+
+ // No display options in recent directories
+ if (picking && details.isInRecents()) {
+ final MenuItem grid = menu.findItem(R.id.menu_grid);
+ final MenuItem list = menu.findItem(R.id.menu_list);
+ grid.setVisible(false);
+ list.setVisible(false);
+ }
+
+ fileSize.setVisible(fileSize.isVisible() && !picking);
+
+ Menus.disableHiddenItems(menu);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index e5de6ba..59b9e9b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -36,10 +36,13 @@
import android.view.Menu;
import android.view.MenuItem;
+import com.android.documentsui.MenuManager.DirectoryDetails;
import com.android.documentsui.OperationDialogFragment.DialogType;
import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
+import com.android.documentsui.dirlist.FragmentTuner;
+import com.android.documentsui.dirlist.FragmentTuner.FilesTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
@@ -67,6 +70,8 @@
// We use the time gap to figure out whether to close app or reopen the drawer.
private long mDrawerLastFiddled;
private DocumentClipper mClipper;
+ private FilesMenuManager mMenuManager;
+ private DirectoryDetails mDetails;
public FilesActivity() {
super(R.layout.files_activity, TAG);
@@ -77,6 +82,13 @@
super.onCreate(icicle);
mClipper = DocumentsApplication.getDocumentClipper(this);
+ mMenuManager = new FilesMenuManager(mSearchManager);
+ mDetails = new DirectoryDetails(this) {
+ @Override
+ public boolean hasItemsToPaste() {
+ return mClipper.hasItemsToPaste();
+ }
+ };
RootsFragment.show(getFragmentManager(), null);
@@ -198,23 +210,7 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
-
- final RootInfo root = getCurrentRoot();
-
- final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
- final MenuItem settings = menu.findItem(R.id.menu_settings);
- final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
-
- createDir.setVisible(true);
- createDir.setEnabled(canCreateDirectory());
- pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
- settings.setVisible(root.hasSettings());
- newWindow.setVisible(Shared.shouldShowFancyFeatures(this));
-
- Menus.disableHiddenItems(menu, pasteFromCb);
- // It hides icon if searching in progress
- mSearchManager.updateMenu();
+ mMenuManager.updateOptionMenu(menu, mDetails);
return true;
}
@@ -462,6 +458,16 @@
finish();
}
+ @Override
+ public FragmentTuner createFragmentTuner() {
+ return new FilesTuner(this, getDisplayState());
+ }
+
+ @Override
+ public MenuManager getMenuManager() {
+ return mMenuManager;
+ }
+
/**
* Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
* to know which root to select. Also, the stack doesn't contain intermediate directories.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesMenuManager.java b/packages/DocumentsUI/src/com/android/documentsui/FilesMenuManager.java
new file mode 100644
index 0000000..f7256ff
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesMenuManager.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.android.documentsui.R;
+
+final class FilesMenuManager implements MenuManager {
+
+ private final SearchViewManager mSearchManager;
+
+ public FilesMenuManager(SearchViewManager searchManager) {
+ mSearchManager = searchManager;
+ }
+
+ @Override
+ public void updateActionMenu(Menu menu, SelectionDetails selection) {
+
+ menu.findItem(R.id.menu_open).setVisible(false); // "open" is never used in Files.
+
+ // Commands accessible only via keyboard...
+ MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
+ MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
+
+ // Commands visible in the UI...
+ MenuItem rename = menu.findItem(R.id.menu_rename);
+ MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+ MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
+ MenuItem share = menu.findItem(R.id.menu_share);
+ MenuItem delete = menu.findItem(R.id.menu_delete);
+
+ // Commands usually on action-bar, so we always manage visibility.
+ share.setVisible(!selection.containsDirectories() && !selection.containsPartialFiles());
+ delete.setVisible(selection.canDelete());
+
+ // Commands always in overflow, so we don't bother showing/hiding...
+ copyTo.setVisible(true);
+ moveTo.setVisible(true);
+ rename.setVisible(true);
+
+ // copy is not visible, keyboard only
+ copy.setEnabled(!selection.containsPartialFiles());
+
+ copyTo.setEnabled(!selection.containsPartialFiles());
+ moveTo.setEnabled(!selection.containsPartialFiles() && selection.canDelete());
+ rename.setEnabled(!selection.containsPartialFiles() && selection.canRename());
+
+ Menus.disableHiddenItems(menu, copy, paste);
+ }
+
+ @Override
+ public void updateOptionMenu(Menu menu, DirectoryDetails details) {
+
+ final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+ final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
+ final MenuItem settings = menu.findItem(R.id.menu_settings);
+ final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
+
+ createDir.setVisible(true);
+ createDir.setEnabled(details.canCreateDirectory());
+ pasteFromCb.setEnabled(details.hasItemsToPaste());
+ settings.setVisible(details.hasRootSettings());
+ newWindow.setVisible(details.shouldShowFancyFeatures());
+
+ Menus.disableHiddenItems(menu, pasteFromCb);
+
+ // It hides icon if searching in progress
+ mSearchManager.updateMenu();
+ }
+
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MenuManager.java b/packages/DocumentsUI/src/com/android/documentsui/MenuManager.java
new file mode 100644
index 0000000..b14fab3
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/MenuManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.view.Menu;
+
+public interface MenuManager {
+
+ /** @See DirectoryFragment.SelectionModeListener#updateActionMenu */
+ void updateActionMenu(Menu mMenu, MenuManager.SelectionDetails selectionDetails);
+ /** @See Activity#onPrepareOptionsMenu */
+ void updateOptionMenu(Menu menu, DirectoryDetails details);
+
+ /**
+ * Access to meta data about the selection.
+ */
+ interface SelectionDetails {
+ boolean containsDirectories();
+ boolean containsPartialFiles();
+
+ // TODO: Update these to express characteristics instead of answering concrete questions,
+ // since the answer to those questions is (or can be) activity specific.
+ boolean canDelete();
+ boolean canRename();
+ }
+
+ public static class DirectoryDetails {
+ private final BaseActivity mActivity;
+
+ public DirectoryDetails(BaseActivity activity) {
+ mActivity = activity;
+ }
+
+ public boolean shouldShowFancyFeatures() {
+ return Shared.shouldShowFancyFeatures(mActivity);
+ }
+
+ public boolean hasRootSettings() {
+ return mActivity.getCurrentRoot().hasSettings();
+ }
+
+ public boolean hasItemsToPaste() {
+ return false;
+ }
+
+ public boolean isInRecents() {
+ return mActivity.getCurrentDirectory() == null;
+ }
+
+ public boolean canCreateDirectory() {
+ return mActivity.canCreateDirectory();
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 11b8891..46a14e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -37,7 +37,7 @@
/**
* Manages searching UI behavior.
*/
-final class SearchViewManager implements
+public class SearchViewManager implements
SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
OnActionExpandListener {
@@ -46,7 +46,7 @@
void onSearchFinished();
}
- public static final String TAG = "SearchManger";
+ private static final String TAG = "SearchManager";
private SearchManagerListener mListener;
private boolean mSearchExpanded;
@@ -129,7 +129,7 @@
&& ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
}
- void showMenu(boolean visible) {
+ protected void showMenu(boolean visible) {
if (mMenuItem == null) {
if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
return;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index da0b347..303db2e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -85,6 +85,7 @@
import com.android.documentsui.Events;
import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.ItemDragListener;
+import com.android.documentsui.MenuManager;
import com.android.documentsui.Menus;
import com.android.documentsui.MessageBar;
import com.android.documentsui.Metrics;
@@ -181,6 +182,7 @@
private @Nullable ActionMode mActionMode;
private DirectoryDragListener mOnDragListener;
+ private MenuManager mMenuManager;
/**
* A callback to show snackbar at the beginning of moving and copying.
@@ -320,7 +322,9 @@
// Make sure this is done after the RecyclerView is set up.
mFocusManager = new FocusManager(context, mRecView, mModel);
- mTuner = FragmentTuner.pick(getContext(), state);
+ final BaseActivity activity = getBaseActivity();
+ mTuner = activity.createFragmentTuner();
+ mMenuManager = activity.getMenuManager();
mClipper = DocumentsApplication.getDocumentClipper(getContext());
final ActivityManager am = (ActivityManager) context.getSystemService(
@@ -504,7 +508,7 @@
* and clearing selection when action mode is explicitly exited by the user.
*/
private final class SelectionModeListener implements MultiSelectManager.Callback,
- ActionMode.Callback, FragmentTuner.SelectionDetails {
+ ActionMode.Callback, MenuManager.SelectionDetails {
private Selection mSelected = new Selection();
@@ -680,7 +684,7 @@
private void updateActionMenu() {
assert(mMenu != null);
- mTuner.updateActionMenu(mMenu, this);
+ mMenuManager.updateActionMenu(mMenu, this);
Menus.disableHiddenItems(mMenu);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index 7b0510b..e175331 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -49,16 +49,6 @@
mState = state;
}
- public static FragmentTuner pick(Context context, State state) {
- switch (state.action) {
- case ACTION_BROWSE:
- return new FilesTuner(context, state);
- default:
- return new DocumentsTuner(context, state);
- }
- }
-
-
// Subtly different from isDocumentEnabled. The reason may be illuminated as follows.
// A folder is enabled such that it may be double clicked, even in settings
// when the folder itself cannot be selected. This may also be true of container types.
@@ -85,13 +75,12 @@
return false;
}
- abstract void updateActionMenu(Menu menu, SelectionDetails selection);
abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch);
/**
* Provides support for Platform specific specializations of DirectoryFragment.
*/
- private static final class DocumentsTuner extends FragmentTuner {
+ public static final class DocumentsTuner extends FragmentTuner {
// We use this to keep track of whether a model has been previously loaded or not so we can
// open the drawer on empty directories on first launch
@@ -147,25 +136,6 @@
}
@Override
- public void updateActionMenu(Menu menu, SelectionDetails selection) {
-
- MenuItem open = menu.findItem(R.id.menu_open);
- MenuItem share = menu.findItem(R.id.menu_share);
- MenuItem delete = menu.findItem(R.id.menu_delete);
- MenuItem rename = menu.findItem(R.id.menu_rename);
- MenuItem selectAll = menu.findItem(R.id.menu_select_all);
-
- open.setVisible(mState.action == ACTION_GET_CONTENT
- || mState.action == ACTION_OPEN);
- share.setVisible(false);
- delete.setVisible(false);
- rename.setVisible(false);
- selectAll.setVisible(mState.allowMultiple);
-
- Menus.disableHiddenItems(menu);
- }
-
- @Override
void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
boolean showDrawer = false;
@@ -196,7 +166,7 @@
/**
* Provides support for Files activity specific specializations of DirectoryFragment.
*/
- private static final class FilesTuner extends FragmentTuner {
+ public static final class FilesTuner extends FragmentTuner {
// We use this to keep track of whether a model has been previously loaded or not so we can
// open the drawer on empty directories on first launch
@@ -206,43 +176,7 @@
super(context, state);
}
- @Override
- public void updateActionMenu(Menu menu, SelectionDetails selection) {
- menu.findItem(R.id.menu_open).setVisible(false); // "open" is never used in Files.
-
- // Commands accessible only via keyboard...
- MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
- MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
-
- // Commands visible in the UI...
- MenuItem rename = menu.findItem(R.id.menu_rename);
- MenuItem moveTo = menu.findItem(R.id.menu_move_to);
- MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
- MenuItem share = menu.findItem(R.id.menu_share);
- MenuItem delete = menu.findItem(R.id.menu_delete);
-
- // copy is not visible, keyboard only
- copy.setEnabled(!selection.containsPartialFiles());
-
- // Commands usually on action-bar, so we always manage visibility.
- share.setVisible(!selection.containsDirectories() && !selection.containsPartialFiles());
- delete.setVisible(selection.canDelete());
-
- share.setEnabled(!selection.containsDirectories() && !selection.containsPartialFiles());
- delete.setEnabled(selection.canDelete());
-
- // Commands always in overflow, so we don't bother showing/hiding...
- copyTo.setVisible(true);
- moveTo.setVisible(true);
- rename.setVisible(true);
-
- copyTo.setEnabled(!selection.containsPartialFiles());
- moveTo.setEnabled(!selection.containsPartialFiles() && selection.canDelete());
- rename.setEnabled(!selection.containsPartialFiles() && selection.canRename());
-
- Menus.disableHiddenItems(menu, copy, paste);
- }
@Override
void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
@@ -271,17 +205,4 @@
return true;
}
}
-
- /**
- * Access to meta data about the selection.
- */
- interface SelectionDetails {
- boolean containsDirectories();
- boolean containsPartialFiles();
-
- // TODO: Update these to express characteristics instead of answering concrete questions,
- // since the answer to those questions is (or can be) activity specific.
- boolean canDelete();
- boolean canRename();
- }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java
new file mode 100644
index 0000000..e0c99d8
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.documentsui.testing.TestDirectoryDetails;
+import com.android.documentsui.testing.TestMenu;
+import com.android.documentsui.testing.TestMenuItem;
+import com.android.documentsui.testing.TestSearchViewManager;
+import com.android.documentsui.testing.TestSelectionDetails;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class DocumentsMenuManagerTest {
+
+ private TestMenu testMenu;
+ private TestMenuItem open;
+ private TestMenuItem share;
+ private TestMenuItem delete;
+ private TestMenuItem rename;
+ private TestMenuItem selectAll;
+ private TestMenuItem createDir;
+ private TestMenuItem fileSize;
+ private TestMenuItem grid;
+ private TestMenuItem list;
+
+ private TestSelectionDetails selectionDetails;
+ private TestDirectoryDetails directoryDetails;
+ private TestSearchViewManager testSearchManager;
+ private State state = new State();
+
+ @Before
+ public void setUp() {
+ testMenu = TestMenu.create();
+ open = testMenu.findItem(R.id.menu_open);
+ share = testMenu.findItem(R.id.menu_share);
+ delete = testMenu.findItem(R.id.menu_delete);
+ rename = testMenu.findItem(R.id.menu_rename);
+ selectAll = testMenu.findItem(R.id.menu_select_all);
+ createDir = testMenu.findItem(R.id.menu_create_dir);
+ fileSize = testMenu.findItem(R.id.menu_file_size);
+ grid = testMenu.findItem(R.id.menu_grid);
+ list = testMenu.findItem(R.id.menu_list);
+
+ selectionDetails = new TestSelectionDetails();
+ directoryDetails = new TestDirectoryDetails();
+ testSearchManager = new TestSearchViewManager();
+ state.action = ACTION_CREATE;
+ state.allowMultiple = true;
+ }
+
+ @Test
+ public void testActionMenu() {
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ open.assertInvisible();
+ delete.assertInvisible();
+ share.assertInvisible();
+ rename.assertInvisible();
+ selectAll.assertVisible();
+ }
+
+ @Test
+ public void testActionMenu_openAction() {
+ state.action = ACTION_OPEN;
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ open.assertVisible();
+ }
+
+
+ @Test
+ public void testActionMenu_notAllowMultiple() {
+ state.allowMultiple = false;
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ selectAll.assertInvisible();
+ }
+
+ @Test
+ public void testOptionMenu() {
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ createDir.assertDisabled();
+ fileSize.assertInvisible();
+ assertTrue(testSearchManager.showMenuCalled());
+ }
+
+ @Test
+ public void testOptionMenu_notPicking() {
+ state.action = ACTION_OPEN;
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ createDir.assertInvisible();
+ grid.assertInvisible();
+ list.assertInvisible();
+ assertFalse(testSearchManager.showMenuCalled());
+ }
+
+ @Test
+ public void testOptionMenu_canCreateDirectory() {
+ directoryDetails.canCreateDirectory = true;
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ createDir.assertEnabled();
+ }
+
+ @Test
+ public void testOptionMenu_inRecents() {
+ directoryDetails.isInRecents = true;
+ DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ grid.assertInvisible();
+ list.assertInvisible();
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesMenuManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesMenuManagerTest.java
new file mode 100644
index 0000000..244f7ec
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesMenuManagerTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.documentsui.testing.TestDirectoryDetails;
+import com.android.documentsui.testing.TestMenu;
+import com.android.documentsui.testing.TestMenuItem;
+import com.android.documentsui.testing.TestSearchViewManager;
+import com.android.documentsui.testing.TestSelectionDetails;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class FilesMenuManagerTest {
+
+ private TestMenu testMenu;
+ private TestMenuItem rename;
+ private TestMenuItem moveTo;
+ private TestMenuItem copyTo;
+ private TestMenuItem share;
+ private TestMenuItem delete;
+ private TestMenuItem createDir;
+ private TestMenuItem pasteFromCb;
+ private TestMenuItem settings;
+ private TestMenuItem newWindow;
+ private TestSelectionDetails selectionDetails;
+ private TestDirectoryDetails directoryDetails;
+ private TestSearchViewManager testSearchManager;
+
+ @Before
+ public void setUp() {
+ testMenu = TestMenu.create();
+ rename = testMenu.findItem(R.id.menu_rename);
+ moveTo = testMenu.findItem(R.id.menu_move_to);
+ copyTo = testMenu.findItem(R.id.menu_copy_to);
+ share = testMenu.findItem(R.id.menu_share);
+ delete = testMenu.findItem(R.id.menu_delete);
+ createDir = testMenu.findItem(R.id.menu_create_dir);
+ pasteFromCb = testMenu.findItem(R.id.menu_paste_from_clipboard);
+ settings = testMenu.findItem(R.id.menu_settings);
+ newWindow = testMenu.findItem(R.id.menu_new_window);
+
+ selectionDetails = new TestSelectionDetails();
+ directoryDetails = new TestDirectoryDetails();
+ testSearchManager = new TestSearchViewManager();
+ }
+
+ @Test
+ public void testActionMenu() {
+ selectionDetails.canDelete = true;
+ selectionDetails.canRename = true;
+
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ rename.assertEnabled();
+ delete.assertVisible();
+ share.assertVisible();
+ copyTo.assertEnabled();
+ moveTo.assertEnabled();
+ }
+
+ @Test
+ public void testActionMenu_containsPartial() {
+ selectionDetails.containPartial = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ rename.assertDisabled();
+ share.assertInvisible();
+ copyTo.assertDisabled();
+ moveTo.assertDisabled();
+ }
+
+ @Test
+ public void testActionMenu_cantRename() {
+ selectionDetails.canRename = false;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ rename.assertDisabled();
+ }
+
+ @Test
+ public void testActionMenu_cantDelete() {
+ selectionDetails.canDelete = false;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ delete.assertInvisible();
+ // We shouldn't be able to move files if we can't delete them
+ moveTo.assertDisabled();
+ }
+
+ @Test
+ public void testActionMenu_containsDirectory() {
+ selectionDetails.containDirectories = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateActionMenu(testMenu, selectionDetails);
+
+ // We can't share directories
+ share.assertInvisible();
+ }
+
+ @Test
+ public void testOptionMenu_canCreateDirectory() {
+ directoryDetails.canCreateDirectory = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ createDir.assertEnabled();
+ }
+
+ @Test
+ public void testOptionMenu_hasItemsToPaste() {
+ directoryDetails.hasItemsToPaste = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ pasteFromCb.assertEnabled();
+ }
+
+ @Test
+ public void testOptionMenu_hasRootSettings() {
+ directoryDetails.hasRootSettings = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ settings.assertVisible();
+ }
+
+ @Test
+ public void testOptionMenu_shouldShowFancyFeatures() {
+ directoryDetails.shouldShowFancyFeatures = true;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager);
+ mgr.updateOptionMenu(testMenu, directoryDetails);
+
+ newWindow.assertVisible();
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestDirectoryDetails.java b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestDirectoryDetails.java
new file mode 100644
index 0000000..ce2ff36
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestDirectoryDetails.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.testing;
+
+import com.android.documentsui.MenuManager.DirectoryDetails;
+
+/**
+ * Test copy of DirectoryDetails, everything default to false
+ */
+public class TestDirectoryDetails extends DirectoryDetails {
+
+ public boolean shouldShowFancyFeatures;
+ public boolean isInRecents;
+ public boolean hasRootSettings;
+ public boolean hasItemsToPaste;
+ public boolean canCreateDirectory;
+
+ public TestDirectoryDetails() {
+ super(null);
+ }
+
+ @Override
+ public boolean shouldShowFancyFeatures() {
+ return shouldShowFancyFeatures;
+ }
+
+ @Override
+ public boolean hasRootSettings() {
+ return hasRootSettings;
+ }
+
+ @Override
+ public boolean hasItemsToPaste() {
+ return hasItemsToPaste;
+ }
+
+ @Override
+ public boolean isInRecents() {
+ return isInRecents;
+ }
+
+ @Override
+ public boolean canCreateDirectory() {
+ return canCreateDirectory;
+ }
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenu.java b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenu.java
new file mode 100644
index 0000000..78f26df
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenu.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.testing;
+
+import android.util.SparseArray;
+import android.view.Menu;
+
+import com.android.documentsui.R;
+
+import org.mockito.Mockito;
+
+/**
+ *
+ * Test copy of {@link android.view.Menu}.
+ *
+ * We use abstract so we don't have to implement all the necessary methods from the interface,
+ * and we use Mockito to just mock out the methods we need.
+ * To get an instance, use {@link #create(int...)}.
+ */
+public abstract class TestMenu implements Menu {
+
+ private SparseArray<TestMenuItem> items = new SparseArray<>();
+
+ public static TestMenu create() {
+ return create(R.id.menu_open,
+ R.id.menu_rename,
+ R.id.menu_copy_to_clipboard,
+ R.id.menu_paste_from_clipboard,
+ R.id.menu_move_to,
+ R.id.menu_copy_to,
+ R.id.menu_share,
+ R.id.menu_delete,
+ R.id.menu_create_dir,
+ R.id.menu_paste_from_clipboard,
+ R.id.menu_settings,
+ R.id.menu_new_window,
+ R.id.menu_select_all,
+ R.id.menu_file_size,
+ R.id.menu_grid,
+ R.id.menu_list);
+ }
+
+ public static TestMenu create(int... ids) {
+ final TestMenu menu = Mockito.mock(TestMenu.class,
+ Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
+ menu.items = new SparseArray<>();
+ for (int id : ids) {
+ TestMenuItem item = TestMenuItem.create(id);
+ menu.addMenuItem(id, item);
+ }
+ return menu;
+ }
+
+ public void addMenuItem(int id, TestMenuItem item) {
+ items.put(id, item);
+ }
+
+ @Override
+ public TestMenuItem findItem(int id) {
+ return items.get(id);
+ }
+
+ @Override
+ public int size() {
+ return items.size();
+ }
+
+ @Override
+ public TestMenuItem getItem(int index) {
+ return items.valueAt(index);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenuItem.java b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenuItem.java
new file mode 100644
index 0000000..406f808
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestMenuItem.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.testing;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.view.MenuItem;
+
+import org.mockito.Mockito;
+
+/**
+*
+* Test copy of {@link android.view.MenuItem}.
+*
+* We use abstract so we don't have to implement all the necessary methods from the interface,
+* and we use Mockito to just mock out the methods we need.
+* To get an instance, use {@link #create(int)}.
+*/
+
+public abstract class TestMenuItem implements MenuItem {
+
+ boolean enabled;
+ boolean visible;
+
+ public static TestMenuItem create(int id) {
+ final TestMenuItem mockMenuItem = Mockito.mock(TestMenuItem.class,
+ Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
+
+ return mockMenuItem;
+ }
+
+ @Override
+ public MenuItem setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+ @Override
+ public MenuItem setVisible(boolean visible) {
+ this.visible = visible;
+ return this;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return this.visible;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void assertEnabled() {
+ assertTrue(this.enabled);
+ }
+
+ public void assertDisabled() {
+ assertFalse(this.enabled);
+ }
+
+ public void assertVisible() {
+ assertTrue(this.visible);
+ }
+
+ public void assertInvisible() {
+ assertFalse(this.visible);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSearchViewManager.java b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSearchViewManager.java
new file mode 100644
index 0000000..29ae3bd
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSearchViewManager.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.testing;
+
+import android.os.Bundle;
+
+import com.android.documentsui.SearchViewManager;
+
+/**
+ * Test copy of {@link com.android.documentsui.SearchViewManager}
+ *
+ * Specficially used to test whether {@link #showMenu(boolean)}
+ * and {@link #updateMenu()} are called.
+ */
+public class TestSearchViewManager extends SearchViewManager {
+
+ boolean updateMenuCalled;
+ boolean showMenuCalled;
+
+ public TestSearchViewManager(SearchManagerListener listener, Bundle savedState) {
+ super(listener, savedState);
+ }
+
+ public TestSearchViewManager() {
+ super(null, null);
+ }
+
+ @Override
+ protected void showMenu(boolean visible) {
+ showMenuCalled = true;
+ }
+
+ @Override
+ public void updateMenu() {
+ updateMenuCalled = true;
+ }
+
+ public boolean showMenuCalled() {
+ return showMenuCalled;
+ }
+
+ public boolean updateMenuCalled() {
+ return updateMenuCalled;
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSelectionDetails.java b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSelectionDetails.java
new file mode 100644
index 0000000..20796ca
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/testing/TestSelectionDetails.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.testing;
+
+import com.android.documentsui.MenuManager.SelectionDetails;
+
+/**
+ * Test copy of SelectionDetails, everything default to false
+ */
+public class TestSelectionDetails implements SelectionDetails {
+
+ public boolean canRename;
+ public boolean canDelete;
+ public boolean containPartial;
+ public boolean containDirectories;
+
+ @Override
+ public boolean containsPartialFiles() {
+ return containPartial;
+ }
+
+ @Override
+ public boolean containsDirectories() {
+ return containDirectories;
+ }
+
+ @Override
+ public boolean canRename() {
+ return canRename;
+ }
+
+ @Override
+ public boolean canDelete() {
+ return canDelete;
+ }
+ }
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index 5ca4857..0b08a55 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/qs_detail_items_padding_top"
- android:paddingStart="16dp"
+ android:paddingStart="@dimen/qs_detail_padding_start"
android:paddingEnd="16dp">
<com.android.systemui.qs.AutoSizingList
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cb3f080..ef3d550 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,6 +215,7 @@
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
<dimen name="qs_battery_padding">2dp</dimen>
+ <dimen name="qs_detail_padding_start">16dp</dimen>
<dimen name="qs_detail_items_padding_top">4dp</dimen>
<dimen name="qs_detail_item_icon_size">24dp</dimen>
<dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index dba7130..bfa43fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -42,6 +42,7 @@
private float mViewAlpha = 1.0f;
private ValueAnimator mAlphaAnimator;
private Rect mExcludedRect = new Rect();
+ private int mLeftInset = 0;
private boolean mHasExcludedArea;
private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@@ -87,12 +88,12 @@
if (mExcludedRect.top > 0) {
canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
}
- if (mExcludedRect.left > 0) {
- canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
- mPaint);
+ if (mExcludedRect.left + mLeftInset > 0) {
+ canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left + mLeftInset,
+ mExcludedRect.bottom, mPaint);
}
- if (mExcludedRect.right < getWidth()) {
- canvas.drawRect(mExcludedRect.right,
+ if (mExcludedRect.right + mLeftInset < getWidth()) {
+ canvas.drawRect(mExcludedRect.right + mLeftInset,
mExcludedRect.top,
getWidth(),
mExcludedRect.bottom,
@@ -183,4 +184,14 @@
public void setChangeRunnable(Runnable changeRunnable) {
mChangeRunnable = changeRunnable;
}
+
+ public void setLeftInset(int leftInset) {
+ if (mLeftInset != leftInset) {
+ mLeftInset = leftInset;
+
+ if (mHasExcludedArea) {
+ invalidate();
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 061586d..94ede0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -518,6 +518,10 @@
mScrimBehind.setExcludedArea(area);
}
+ public void setLeftInset(int inset) {
+ mScrimBehind.setLeftInset(inset);
+ }
+
public int getScrimBehindColor() {
return mScrimBehind.getScrimColorWithAlpha();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ebfa018..7b22b88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -70,6 +70,7 @@
private View mBrightnessMirror;
private int mRightInset = 0;
+ private int mLeftInset = 0;
private PhoneStatusBar mService;
private final Paint mTransparentSrcPaint = new Paint();
@@ -93,25 +94,26 @@
@Override
protected boolean fitSystemWindows(Rect insets) {
if (getFitsSystemWindows()) {
- boolean paddingChanged = insets.left != getPaddingLeft()
- || insets.top != getPaddingTop()
+ boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
// Super-special right inset handling, because scrims and backdrop need to ignore it.
- if (insets.right != mRightInset) {
+ if (insets.right != mRightInset || insets.left != mLeftInset) {
mRightInset = insets.right;
+ mLeftInset = insets.left;
applyMargins();
}
- // Drop top inset, apply left inset and pass through bottom inset.
+ // Drop top inset, and pass through bottom inset.
if (paddingChanged) {
- setPadding(insets.left, 0, 0, 0);
+ setPadding(0, 0, 0, 0);
}
insets.left = 0;
insets.top = 0;
insets.right = 0;
} else {
- if (mRightInset != 0) {
+ if (mRightInset != 0 || mLeftInset != 0) {
mRightInset = 0;
+ mLeftInset = 0;
applyMargins();
}
boolean changed = getPaddingLeft() != 0
@@ -127,13 +129,16 @@
}
private void applyMargins() {
+ mService.mScrimController.setLeftInset(mLeftInset);
final int N = getChildCount();
for (int i = 0; i < N; i++) {
View child = getChildAt(i);
if (child.getLayoutParams() instanceof LayoutParams) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.ignoreRightInset && lp.rightMargin != mRightInset) {
+ if (!lp.ignoreRightInset
+ && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) {
lp.rightMargin = mRightInset;
+ lp.leftMargin = mLeftInset;
child.requestLayout();
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 15ce017..762e170 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -262,6 +262,10 @@
private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
"com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+ private static final int NAV_BAR_BOTTOM = 0;
+ private static final int NAV_BAR_RIGHT = 1;
+ private static final int NAV_BAR_LEFT = 2;
+
/**
* Keyguard stuff
*/
@@ -354,9 +358,8 @@
int mStatusBarHeight;
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
- boolean mCanHideNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
- boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
+ int mNavigationBarPosition = NAV_BAR_BOTTOM;
int[] mNavigationBarHeightForRotationDefault = new int[4];
int[] mNavigationBarWidthForRotationDefault = new int[4];
int[] mNavigationBarHeightForRotationInCarMode = new int[4];
@@ -1683,13 +1686,19 @@
}
@Override
public void onSwipeFromBottom() {
- if (mNavigationBar != null && mNavigationBarOnBottom) {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
requestTransientBars(mNavigationBar);
}
}
@Override
public void onSwipeFromRight() {
- if (mNavigationBar != null && !mNavigationBarOnBottom) {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromLeft() {
+ if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
requestTransientBars(mNavigationBar);
}
}
@@ -2778,8 +2787,8 @@
if (win.getAttrs().windowAnimations != 0) {
return 0;
}
- // This can be on either the bottom or the right.
- if (mNavigationBarOnBottom) {
+ // This can be on either the bottom or the right or the left.
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
return R.anim.dock_bottom_exit;
@@ -2787,7 +2796,7 @@
|| transit == TRANSIT_SHOW) {
return R.anim.dock_bottom_enter;
}
- } else {
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
return R.anim.dock_right_exit;
@@ -2795,6 +2804,14 @@
|| transit == TRANSIT_SHOW) {
return R.anim.dock_right_enter;
}
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ if (transit == TRANSIT_EXIT
+ || transit == TRANSIT_HIDE) {
+ return R.anim.dock_left_exit;
+ } else if (transit == TRANSIT_ENTER
+ || transit == TRANSIT_SHOW) {
+ return R.anim.dock_left_enter;
+ }
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
return selectDockedDividerAnimationLw(win, transit);
@@ -2823,10 +2840,12 @@
// If the divider is behind the navigation bar, don't animate.
final Rect frame = win.getFrameLw();
final boolean behindNavBar = mNavigationBar != null
- && ((mNavigationBarOnBottom
+ && ((mNavigationBarPosition == NAV_BAR_BOTTOM
&& frame.top + insets >= mNavigationBar.getFrameLw().top)
- || (!mNavigationBarOnBottom
- && frame.left + insets >= mNavigationBar.getFrameLw().left));
+ || (mNavigationBarPosition == NAV_BAR_RIGHT
+ && frame.left + insets >= mNavigationBar.getFrameLw().left)
+ || (mNavigationBarPosition == NAV_BAR_LEFT
+ && frame.right - insets <= mNavigationBar.getFrameLw().right));
final boolean landscape = frame.height() > frame.width();
final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
|| frame.left + insets >= win.getDisplayFrameLw().right);
@@ -4019,7 +4038,7 @@
navVisible |= !canHideNavigationBar();
boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
- displayRotation, uiMode, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+ displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
navAllowedHidden, statusBarExpandedNotKeyguard);
if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
@@ -4098,8 +4117,8 @@
}
private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
- int uiMode, int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
- boolean navTranslucent, boolean navAllowedHidden,
+ int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+ boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
if (mNavigationBar != null) {
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
@@ -4107,8 +4126,9 @@
// size. We need to do this directly, instead of relying on
// it to bubble up from the nav bar, because this needs to
// change atomically with screen rotations.
- mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
- if (mNavigationBarOnBottom) {
+ mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
+ displayRotation);
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
int top = displayHeight - overscanBottom
- getNavigationBarHeight(displayRotation, uiMode);
@@ -4134,7 +4154,7 @@
// we can tell the app that it is covered by it.
mSystemBottom = mTmpNavigationFrame.top;
}
- } else {
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
int left = displayWidth - overscanRight
- getNavigationBarWidth(displayRotation, uiMode);
@@ -4160,6 +4180,33 @@
// we can tell the app that it is covered by it.
mSystemRight = mTmpNavigationFrame.left;
}
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ // Seascape screen; nav bar goes to the left.
+ int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
+ mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
+ mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockLeft = mTmpNavigationFrame.right;
+ // TODO: not so sure about those:
+ mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
+ mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+ mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status
+ // bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemLeft = mTmpNavigationFrame.right;
+ }
}
// Make sure the content and current rectangles are updated to
// account for the restrictions from the navigation bar.
@@ -4180,8 +4227,15 @@
return false;
}
- private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
- return !mNavigationBarCanMove || displayWidth < displayHeight;
+ private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+ if (mNavigationBarCanMove && displayWidth > displayHeight) {
+ if (displayRotation == Surface.ROTATION_270) {
+ return NAV_BAR_LEFT;
+ } else {
+ return NAV_BAR_RIGHT;
+ }
+ }
+ return NAV_BAR_BOTTOM;
}
/** {@inheritDoc} */
@@ -4357,7 +4411,11 @@
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
- pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+ }
}
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
@@ -6464,10 +6522,13 @@
// Only navigation bar
if (mNavigationBar != null) {
- if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+ int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+ if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
- } else {
+ } else if (position == NAV_BAR_RIGHT) {
outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+ } else if (position == NAV_BAR_LEFT) {
+ outInsets.left = getNavigationBarWidth(displayRotation, mUiMode);
}
}
}
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 80e4341..598c58e 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -43,6 +43,7 @@
private static final int SWIPE_FROM_TOP = 1;
private static final int SWIPE_FROM_BOTTOM = 2;
private static final int SWIPE_FROM_RIGHT = 3;
+ private static final int SWIPE_FROM_LEFT = 4;
private final Context mContext;
private final int mSwipeStartThreshold;
@@ -127,6 +128,9 @@
} else if (swipe == SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
mCallbacks.onSwipeFromRight();
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
+ mCallbacks.onSwipeFromLeft();
}
}
break;
@@ -229,6 +233,11 @@
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT;
}
+ if (fromX <= mSwipeStartThreshold
+ && x > fromX + mSwipeDistanceThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_LEFT;
+ }
return SWIPE_NONE;
}
@@ -265,6 +274,7 @@
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
+ void onSwipeFromLeft();
void onFling(int durationMs);
void onDown();
void onUpOrCancel();