Merge "2D Recents: polish the app -> recents transition" into nyc-mr2-dev
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 1823711..f1e24b8 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -349,6 +349,34 @@
throw new UnsupportedOperationException(
"Writing operation is not supported by the device.");
}
+
+ final int parentObjectHandle;
+ final int storageId;
+ switch (parentId.mDocumentType) {
+ case MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE:
+ final String[] storageDocumentIds =
+ mDatabase.getStorageDocumentIds(parentId.mDocumentId);
+ if (storageDocumentIds.length == 1) {
+ final String newDocumentId =
+ createDocument(storageDocumentIds[0], mimeType, displayName);
+ notifyChildDocumentsChange(parentDocumentId);
+ return newDocumentId;
+ } else {
+ throw new UnsupportedOperationException(
+ "Cannot create a file under the device.");
+ }
+ case MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE:
+ storageId = parentId.mStorageId;
+ parentObjectHandle = -1;
+ break;
+ case MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT:
+ storageId = parentId.mStorageId;
+ parentObjectHandle = parentId.mObjectHandle;
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected document type.");
+ }
+
pipe = ParcelFileDescriptor.createReliablePipe();
int objectHandle = -1;
MtpObjectInfo info = null;
@@ -359,8 +387,8 @@
MtpConstants.FORMAT_ASSOCIATION :
MediaFile.getFormatCode(displayName, mimeType);
info = new MtpObjectInfo.Builder()
- .setStorageId(parentId.mStorageId)
- .setParent(parentId.mObjectHandle)
+ .setStorageId(storageId)
+ .setParent(parentObjectHandle)
.setFormat(formatCode)
.setName(displayName)
.build();
@@ -414,6 +442,24 @@
}
}
+ @Override
+ public boolean isChildDocument(String parentDocumentId, String documentId) {
+ try {
+ Identifier identifier = mDatabase.createIdentifier(documentId);
+ while (true) {
+ if (parentDocumentId.equals(identifier.mDocumentId)) {
+ return true;
+ }
+ if (identifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
+ return false;
+ }
+ identifier = mDatabase.getParentIdentifier(identifier.mDocumentId);
+ }
+ } catch (FileNotFoundException error) {
+ return false;
+ }
+ }
+
void openDevice(int deviceId) throws IOException {
synchronized (mDeviceListLock) {
if (mDeviceToolkits.containsKey(deviceId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index d19b460..8831ae2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -34,6 +34,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Queue;
import java.util.concurrent.TimeoutException;
import static com.android.mtp.MtpDatabase.strings;
@@ -546,7 +548,7 @@
public void testOpenDocument_writing() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
setupRoots(0, new MtpRoot[] {
- new MtpRoot(0, 0, "Storage", 0, 0, "")
+ new MtpRoot(0, 100, "Storage", 0, 0, "")
});
final String documentId = mProvider.createDocument("2", "text/plain", "test.txt");
{
@@ -688,6 +690,29 @@
}
}
+ public void testCreateDocument() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 100, "Storage A", 100, 100, null)
+ });
+ final String documentId = mProvider.createDocument("1", "text/plain", "note.txt");
+ final Uri deviceUri = DocumentsContract.buildChildDocumentsUri(
+ MtpDocumentsProvider.AUTHORITY, "1");
+ final Uri storageUri = DocumentsContract.buildChildDocumentsUri(
+ MtpDocumentsProvider.AUTHORITY, "2");
+ mResolver.waitForNotification(storageUri, 1);
+ mResolver.waitForNotification(deviceUri, 1);
+ try (final Cursor cursor = mProvider.queryDocument(documentId, null)) {
+ assertTrue(cursor.moveToNext());
+ assertEquals(
+ "note.txt",
+ cursor.getString(cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME)));
+ assertEquals(
+ "text/plain",
+ cursor.getString(cursor.getColumnIndex(Document.COLUMN_MIME_TYPE)));
+ }
+ }
+
public void testCreateDocument_noWritingSupport() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
mMtpManager.addValidDevice(new MtpDeviceRecord(
@@ -769,6 +794,18 @@
assertEquals(0x400000000L, cursor.getLong(0));
}
+ public void testIsChildDocument() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
+ setupHierarchyDocuments("1");
+ assertTrue(mProvider.isChildDocument("1", "1"));
+ assertTrue(mProvider.isChildDocument("1", "14"));
+ assertTrue(mProvider.isChildDocument("2", "14"));
+ assertTrue(mProvider.isChildDocument("5", "14"));
+ assertFalse(mProvider.isChildDocument("3", "14"));
+ assertFalse(mProvider.isChildDocument("6", "14"));
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
@@ -822,4 +859,63 @@
return getStrings(mProvider.queryChildDocuments(
parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
}
+
+ static class HierarchyDocument {
+ int depth;
+ String documentId;
+ int objectHandle;
+ int parentHandle;
+
+ HierarchyDocument createChildDocument(int newHandle) {
+ final HierarchyDocument doc = new HierarchyDocument();
+ doc.depth = depth - 1;
+ doc.objectHandle = newHandle;
+ doc.parentHandle = objectHandle;
+ return doc;
+ }
+
+ MtpObjectInfo toObjectInfo() {
+ return new MtpObjectInfo.Builder()
+ .setName("doc_" + documentId)
+ .setFormat(depth > 0 ?
+ MtpConstants.FORMAT_ASSOCIATION : MtpConstants.FORMAT_TEXT)
+ .setObjectHandle(objectHandle)
+ .setParent(parentHandle)
+ .build();
+ }
+ }
+
+ private void setupHierarchyDocuments(String documentId) throws Exception {
+ final Queue<HierarchyDocument> ids = new LinkedList<>();
+ final HierarchyDocument firstDocument = new HierarchyDocument();
+ firstDocument.depth = 3;
+ firstDocument.documentId = documentId;
+ firstDocument.objectHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
+ ids.add(firstDocument);
+
+ int objectHandle = 100;
+ while (!ids.isEmpty()) {
+ final HierarchyDocument document = ids.remove();
+ final HierarchyDocument[] children = new HierarchyDocument[] {
+ document.createChildDocument(objectHandle++),
+ document.createChildDocument(objectHandle++),
+ document.createChildDocument(objectHandle++),
+ };
+ final String[] childDocIds = setupDocuments(
+ 0, 0, document.objectHandle, document.documentId, new MtpObjectInfo[] {
+ children[0].toObjectInfo(),
+ children[1].toObjectInfo(),
+ children[2].toObjectInfo(),
+ });
+ children[0].documentId = childDocIds[0];
+ children[1].documentId = childDocIds[1];
+ children[2].documentId = childDocIds[2];
+
+ if (children[0].depth > 0) {
+ ids.add(children[0]);
+ ids.add(children[1]);
+ ids.add(children[2]);
+ }
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 9a81489..4dba648 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -26,6 +26,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import junit.framework.Assert;
public class TestMtpManager extends MtpManager {
public static final int CREATED_DOCUMENT_HANDLE = 1000;
@@ -151,6 +152,9 @@
@Override
int createDocument(int deviceId, MtpObjectInfo objectInfo, ParcelFileDescriptor source)
throws IOException {
+ Assert.assertNotSame(0, objectInfo.getStorageId());
+ Assert.assertNotSame(-1, objectInfo.getStorageId());
+ Assert.assertNotSame(0, objectInfo.getParent());
final String key = pack(deviceId, CREATED_DOCUMENT_HANDLE);
if (mObjectInfos.containsKey(key)) {
throw new IOException();
diff --git a/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
new file mode 100644
index 0000000..a85beb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#61FFFFFF" />
+ <corners android:radius="8dp"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
index 94ffdb1..0b9836ff 100644
--- a/packages/SystemUI/res/values/dimens_grid.xml
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -21,5 +21,6 @@
<dimen name="recents_grid_padding_task_view">20dp</dimen>
<dimen name="recents_grid_task_view_header_height">44dp</dimen>
<dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
+ <dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 914035b..a7f6b70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -50,7 +50,7 @@
/**
* Returns the task to focus given the current launch state.
*/
- public int getInitialFocusTaskIndex(int numTasks) {
+ public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchedFromApp) {
@@ -66,6 +66,11 @@
return numTasks - 1;
}
+ if (useGridLayout) {
+ // If coming from another app to the grid layout, focus the front most task
+ return numTasks - 1;
+ }
+
// If coming from another app, focus the next task
return Math.max(0, numTasks - 2);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 178cb9f..9b25ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -247,6 +247,9 @@
*/
public static class DockState implements DropTarget {
+ public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+ public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
// The rotation to apply to the hint text
@Retention(RetentionPolicy.SOURCE)
@IntDef({HORIZONTAL, VERTICAL})
@@ -319,7 +322,8 @@
private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
int hintTextResId) {
dockAreaAlpha = areaAlpha;
- dockAreaOverlay = new ColorDrawable(0xFFffffff);
+ dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
+ ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
dockAreaOverlay.setAlpha(0);
hintTextAlpha = hintAlpha;
hintTextOrientation = hintOrientation;
@@ -435,7 +439,7 @@
* @param createMode used to pass to ActivityManager to dock the task
* @param touchArea the area in which touch will initiate this dock state
* @param dockArea the visible dock area
- * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+ * @param expandedTouchDockArea the area in which touch will continue to dock after entering
* the initial touch area. This is also the new dock area to
* draw.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index ed86d4c..a2ee4c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -105,6 +105,7 @@
private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
Interpolators.LINEAR_OUT_SLOW_IN;
+ private final int mEnterAndExitFromHomeTranslationOffset;
private TaskStackView mStackView;
private TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -113,6 +114,8 @@
public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
mStackView = stackView;
+ mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
+ ? 0 : DOUBLE_FRAME_OFFSET_MS;
}
/**
@@ -260,7 +263,7 @@
AnimationProps taskAnimation = new AnimationProps()
.setInitialPlayTime(AnimationProps.BOUNDS,
Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
- DOUBLE_FRAME_OFFSET_MS)
+ mEnterAndExitFromHomeTranslationOffset)
.setStartDelay(AnimationProps.ALPHA,
Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
FRAME_OFFSET_MS)
@@ -321,7 +324,7 @@
AnimationProps taskAnimation;
if (animated) {
int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
- DOUBLE_FRAME_OFFSET_MS;
+ mEnterAndExitFromHomeTranslationOffset;
taskAnimation = new AnimationProps()
.setStartDelay(AnimationProps.BOUNDS, delay)
.setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 87a6846..bc2c424 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -70,6 +70,7 @@
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
@@ -93,6 +94,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.grid.GridTaskView;
+import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -206,6 +208,10 @@
private int mLastWidth;
private int mLastHeight;
+ // We keep track of the task view focused by user interaction and draw a frame around it in the
+ // grid layout.
+ private TaskViewFocusFrame mTaskViewFocusFrame;
+
// A convenience update listener to request updating clipping of tasks
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -265,6 +271,14 @@
mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
mDisplayRect = ssp.getDisplayRect();
+ // Create a frame to draw around the focused task view
+ if (Recents.getConfiguration().isGridEnabled) {
+ mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
+ mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
+ addView(mTaskViewFocusFrame);
+ getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
+ }
+
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
R.integer.recents_task_bar_dismiss_delay_seconds);
mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@@ -878,7 +892,7 @@
*
* @return whether or not the stack will scroll as a part of this focus change
*/
- private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+ public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
final boolean requestViewFocus) {
return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
}
@@ -888,7 +902,7 @@
*
* @return whether or not the stack will scroll as a part of this focus change
*/
- private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+ public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
boolean requestViewFocus, int timerIndicatorDuration) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
@@ -940,6 +954,10 @@
newFocusedTaskView.setFocusedState(true, requestViewFocus);
}
}
+ // Any time a task view gets the focus, we move the focus frame around it.
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
+ }
}
return willScroll;
}
@@ -1005,20 +1023,28 @@
float stackScroll = mStackScroller.getStackScroll();
ArrayList<Task> tasks = mStack.getStackTasks();
int taskCount = tasks.size();
- if (forward) {
- // Walk backwards and focus the next task smaller than the current stack scroll
- for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) <= 0) {
- break;
- }
- }
+ if (useGridLayout()) {
+ // For the grid layout, we directly set focus to the most recently used task
+ // no matter we're moving forwards or backwards.
+ newIndex = taskCount - 1;
} else {
- // Walk forwards and focus the next task larger than the current stack scroll
- for (newIndex = 0; newIndex < taskCount; newIndex++) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) >= 0) {
- break;
+ // For the grid layout we pick a proper task to focus, according to the current
+ // stack scroll.
+ if (forward) {
+ // Walk backwards and focus the next task smaller than the current stack scroll
+ for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
+ float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+ if (Float.compare(taskP, stackScroll) <= 0) {
+ break;
+ }
+ }
+ } else {
+ // Walk forwards and focus the next task larger than the current stack scroll
+ for (newIndex = 0; newIndex < taskCount; newIndex++) {
+ float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+ if (Float.compare(taskP, stackScroll) >= 0) {
+ break;
+ }
}
}
}
@@ -1037,20 +1063,23 @@
/**
* Resets the focused task.
*/
- void resetFocusedTask(Task task) {
+ public void resetFocusedTask(Task task) {
if (task != null) {
TaskView tv = getChildViewForTask(task);
if (tv != null) {
tv.setFocusedState(false, false /* requestViewFocus */);
}
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
mFocusedTask = null;
}
/**
* Returns the focused task.
*/
- Task getFocusedTask() {
+ public Task getFocusedTask() {
return mFocusedTask;
}
@@ -1253,6 +1282,9 @@
for (int i = 0; i < taskViewCount; i++) {
measureTaskView(mTmpTaskViews.get(i));
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.measure();
+ }
setMeasuredDimension(width, height);
mLastWidth = width;
@@ -1287,6 +1319,9 @@
for (int i = 0; i < taskViewCount; i++) {
layoutTaskView(changed, mTmpTaskViews.get(i));
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.layout();
+ }
if (changed) {
if (mStackScroller.isScrollOutOfBounds()) {
@@ -1339,10 +1374,19 @@
// until after the enter-animation
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
- if (focusedTaskIndex != -1) {
- setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
- false /* requestViewFocus */);
+
+ // We set the initial focused task view iff the following conditions are satisfied:
+ // 1. Recents is showing task views in stack layout.
+ // 2. Recents is launched with ALT + TAB.
+ boolean setFocusOnFirstLayout = !useGridLayout() ||
+ Recents.getConfiguration().getLaunchState().launchedWithAltTab;
+ if (setFocusOnFirstLayout) {
+ int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
+ useGridLayout());
+ if (focusedTaskIndex != -1) {
+ setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
+ false /* requestViewFocus */);
+ }
}
updateStackActionButtonVisibility();
}
@@ -1443,6 +1487,11 @@
// Remove the task from the ignored set
removeIgnoreTask(removedTask);
+ // Resize the grid layout task view focus frame
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.resize();
+ }
+
// If requested, relayout with the given animation
if (animation != null) {
updateLayoutAlgorithm(true /* boundScroll */);
@@ -1740,10 +1789,18 @@
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
Interpolators.FAST_OUT_SLOW_IN));
+
+ // Dismiss the grid task view focus frame
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
}
public final void onBusEvent(DismissFocusedTaskViewEvent event) {
if (mFocusedTask != null) {
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
TaskView tv = getChildViewForTask(mFocusedTask);
if (tv != null) {
tv.dismissTask();
@@ -2073,6 +2130,12 @@
mResetToInitialStateWhenResized = true;
}
+ public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+ if (!event.visible && mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
+ }
+
public void reloadOnConfigurationChange() {
mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index aeb85d0..003138f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -342,8 +342,9 @@
mSv.invalidate();
}
- // Reset the focused task after the user has scrolled
- if (!mSv.mTouchExplorationEnabled) {
+ // Reset the focused task after the user has scrolled, but we have no scrolling
+ // in grid layout and therefore we don't want to reset the focus there.
+ if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
mSv.resetFocusedTask(mSv.getFocusedTask());
}
} else if (mActiveTaskView == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 6fc4ad7..70536b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -51,6 +51,9 @@
private float mAppAspectRatio;
private Rect mSystemInsets = new Rect();
+ /** The thickness of the focused task view frame. */
+ private int mFocusedFrameThickness;
+
/**
* When the amount of tasks is determined, the size and position of every task view can be
* decided. Each instance of TaskGridRectInfo store the task view information for a certain
@@ -137,6 +140,9 @@
public void reloadOnConfigurationChange(Context context) {
Resources res = context.getResources();
mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+ mFocusedFrameThickness = res.getDimensionPixelSize(
+ R.dimen.recents_grid_task_view_focused_frame_thickness);
+
mTaskGridRect = new Rect();
mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
@@ -159,6 +165,10 @@
*/
public TaskViewTransform getTransform(int taskIndex, int taskCount,
TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+ if (taskCount == 0) {
+ transformOut.reset();
+ return transformOut;
+ }
TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
mTaskGridRect.set(gridInfo.size);
@@ -174,7 +184,7 @@
// We also need to invert the index in order to display the most recent tasks first.
int taskLayoutIndex = taskCount - taskIndex - 1;
- boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
+ boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
// Fill out the transform
transformOut.scale = 1f;
@@ -223,7 +233,18 @@
return buttonRect;
}
+ public void updateTaskGridRect(int taskCount) {
+ if (taskCount > 0) {
+ TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+ mTaskGridRect.set(gridInfo.size);
+ }
+ }
+
public Rect getTaskGridRect() {
return mTaskGridRect;
}
+
+ public int getFocusFrameThickness() {
+ return mFocusedFrameThickness;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
new file mode 100644
index 0000000..86ed583
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.views.grid;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+
+public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
+
+ private TaskStackView mSv;
+ private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+ public TaskViewFocusFrame(Context context) {
+ this(context, null);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setBackground(mContext.getDrawable(
+ R.drawable.recents_grid_task_view_focus_frame_background));
+ setFocusable(false);
+ hide();
+ }
+
+ public TaskViewFocusFrame(Context context, TaskStackView stackView,
+ TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
+ this(context);
+ mSv = stackView;
+ mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
+ }
+
+ /**
+ * Measure the width and height of the focus frame according to the current grid task view size.
+ */
+ public void measure() {
+ int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+ Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
+ measure(
+ MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
+ }
+
+ /**
+ * Layout the focus frame with its size.
+ */
+ public void layout() {
+ layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ /**
+ * Update the current size of grid task view and the focus frame.
+ */
+ public void resize() {
+ if (mSv.useGridLayout()) {
+ mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
+ measure();
+ requestLayout();
+ }
+ }
+
+ /**
+ * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
+ * it's not an instance of GridTaskView, we hide the focus frame.
+ * @param newFocus The newly focused view.
+ */
+ public void moveGridTaskViewFocus(View newFocus) {
+ if (mSv.useGridLayout()) {
+ // The frame only shows up in the grid layout. It shouldn't show up in the stack
+ // layout including when we're in the split screen.
+ if (newFocus instanceof GridTaskView) {
+ // If the focus goes to a GridTaskView, we show the frame and layout it.
+ int[] location = new int[2];
+ newFocus.getLocationInWindow(location);
+ int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+ setTranslationX(location[0] - thickness);
+ setTranslationY(location[1] - thickness);
+ show();
+ } else {
+ // If focus goes to other views, we hide the frame.
+ hide();
+ }
+ }
+ }
+
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ if (!mSv.useGridLayout()) {
+ return;
+ }
+ if (newFocus == null) {
+ // We're going to touch mode, unset the focus.
+ moveGridTaskViewFocus(null);
+ return;
+ }
+ if (oldFocus == null) {
+ // We're returning from touch mode, set the focus to the previously focused task.
+ final TaskStack stack = mSv.getStack();
+ final int taskCount = stack.getTaskCount();
+ final int k = stack.indexOfStackTask(mSv.getFocusedTask());
+ final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
+ mSv.setFocusedTask(taskIndexToFocus, false, true);
+ }
+ }
+
+ private void show() {
+ setAlpha(1f);
+ }
+
+ private void hide() {
+ setAlpha(0f);
+ }
+}
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index dd95f67..962ac6f 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -95,9 +95,7 @@
pw.println("File-based Encryption: true");
}
- if (isCheckin(args)) {
- reportCachedValues(pw);
- }
+ reportCachedValues(pw);
// TODO: Read /proc/yaffs and report interesting values;
// add configurable (through args) performance test parameters.
@@ -130,15 +128,6 @@
}
}
- private boolean isCheckin(String[] args) {
- for (String opt : args) {
- if ("--checkin".equals(opt)) {
- return true;
- }
- }
- return false;
- }
-
private void reportCachedValues(PrintWriter pw) {
try {
String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index 8c2ee25..8a04d26 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -533,6 +533,8 @@
SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "1");
mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+
+ registerBroadcastReceiver();
}
@Override
@@ -568,7 +570,6 @@
case PHASE_BOOT_COMPLETED:
if (UserManager.isDeviceInDemoMode(getContext())) {
putDeviceInDemoMode();
- registerBroadcastReceiver();
}
break;
}