Refactoring the focus state to be independent of view focus.
- This allows us to ensure that a task is always focused in the stack,
which simplified focus logic, and allows us to unify the accessibility
focus and normal view focus handling.
- Refactoring some focus actions to events
- Adding flags to experiment with fast-toggle.
Change-Id: I5623f2a5584994033f1ebf1760b55e8bd019944b
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bae8017..07c59a9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -136,7 +136,7 @@
<integer name="touch_acceptance_delay">700</integer>
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
- <integer name="recents_task_bar_dismiss_delay_seconds">1</integer>
+ <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
<!-- The min animation duration for animating views that are currently visible. -->
<integer name="recents_filter_animate_current_views_duration">250</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index cdb6b93..6668df9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -37,6 +37,8 @@
public static final boolean EnableTaskFiltering = false;
// Enables dismiss-all
public static final boolean EnableDismissAll = false;
+ // Enables fast-toggling
+ public static final boolean EnableFastToggleRecents = false;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
// This disables the search bar integration
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c416967..0adad85 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -32,6 +32,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
@@ -41,16 +42,21 @@
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -69,6 +75,9 @@
*/
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+ private final static String TAG = "RecentsActivity";
+ private final static boolean DEBUG = false;
+
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
RecentsConfiguration mConfig;
@@ -96,6 +105,14 @@
// Runnable to be executed after we paused ourselves
Runnable mAfterPauseRunnable;
+ // The trigger to automatically launch the current task
+ DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
+ @Override
+ public void run() {
+ boolean dismissed = dismissRecentsToFocusedTask(false);
+ }
+ });
+
/**
* A common Runnable to finish Recents either by calling finish() (with a custom animation) or
* launching Home with some ActivityOptions. Generally we always launch home when we exit
@@ -244,7 +261,24 @@
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
- /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
+ /**
+ * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+ */
+ boolean dismissRecentsToFocusedTask(boolean checkFilteredStackState) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
+ // If we currently have filtered stacks, then unfilter those first
+ if (checkFilteredStackState &&
+ mRecentsView.unfilterFilteredStacks()) return true;
+ // If we have a focused Task, launch that Task now
+ if (mRecentsView.launchFocusedTask()) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+ */
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
RecentsActivityLaunchState launchState = mConfig.getLaunchState();
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -390,6 +424,11 @@
mRecentsView.post(mAfterPauseRunnable);
mAfterPauseRunnable = null;
}
+
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ // Stop the fast-toggle dozer
+ mIterateTrigger.stopDozing();
+ }
}
@Override
@@ -467,22 +506,27 @@
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
- mRecentsView.focusNextTask(!backward);
+ if (backward) {
+ EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+ } else {
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ }
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
}
return true;
}
case KeyEvent.KEYCODE_DPAD_UP: {
- mRecentsView.focusNextTask(true);
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
- mRecentsView.focusNextTask(false);
+ EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
return true;
}
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_FORWARD_DEL: {
- mRecentsView.dismissFocusedTask();
+ EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+
// Keep track of deletions by keyboard
MetricsLogger.histogram(this, "overview_task_dismissed_source",
Constants.Metrics.DismissSourceKeyboard);
@@ -542,6 +586,16 @@
dismissRecentsToFocusedTaskOrHome(true /* checkFilteredStackState */);
}
+ public final void onBusEvent(IterateRecentsEvent event) {
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ mIterateTrigger.poke();
+ }
+
+ public final void onBusEvent(UserInteractionEvent event) {
+ mIterateTrigger.stopDozing();
+ }
+
public final void onBusEvent(HideRecentsEvent event) {
if (event.triggeredFromAltTab) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
@@ -558,7 +612,7 @@
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
- mRecentsView.startEnterRecentsAnimation(ctx);
+ ctx.postAnimationTrigger.increment();
if (mSearchWidgetInfo != null) {
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
@@ -570,6 +624,20 @@
}
});
}
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // If we are not launching with alt-tab and fast-toggle is enabled, then start
+ // the dozer now
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ if (Constants.DebugFlags.App.EnableFastToggleRecents &&
+ !launchState.launchedWithAltTab) {
+ mIterateTrigger.startDozing();
+ }
+ }
+ });
+ mRecentsView.startEnterRecentsAnimation(ctx);
+ ctx.postAnimationTrigger.decrement();
}
public final void onBusEvent(AppWidgetProviderChangedEvent event) {
@@ -589,7 +657,7 @@
MetricsLogger.count(this, "overview_app_info", 1);
}
- public final void onBusEvent(DismissTaskEvent event) {
+ public final void onBusEvent(DismissTaskViewEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 07c7897..cea0b53 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -40,6 +40,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -265,26 +266,38 @@
mTriggeredFromAltTab = false;
try {
- // If the user has toggled it too quickly, then just eat up the event here (it's better
- // than showing a janky screenshot).
- // NOTE: Ideally, the screenshot mechanism would take the window transform into account
- if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
- return;
- }
-
- // If Recents is the front most activity, then we should just communicate with it
- // directly to launch the first task or dismiss itself
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
- // Notify recents to toggle itself
- EventBus.getDefault().post(new ToggleRecentsEvent());
- mLastToggleTime = SystemClock.elapsedRealtime();
+ if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ // Notify recents to move onto the next task
+ EventBus.getDefault().post(new IterateRecentsEvent());
+ } else {
+ // If the user has toggled it too quickly, then just eat up the event here (it's
+ // better than showing a janky screenshot).
+ // NOTE: Ideally, the screenshot mechanism would take the window transform into
+ // account
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+ return;
+ }
+
+ EventBus.getDefault().post(new ToggleRecentsEvent());
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
return;
} else {
+ // If the user has toggled it too quickly, then just eat up the event here (it's
+ // better than showing a janky screenshot).
+ // NOTE: Ideally, the screenshot mechanism would take the window transform into
+ // account
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+ return;
+ }
+
// Otherwise, start the recents activity
startRecentsActivity(topTask, isTopTaskHome.value);
+ mLastToggleTime = SystemClock.elapsedRealtime();
}
} catch (ActivityNotFoundException e) {
Console.logRawError("Failed to launch RecentAppsIntent", e);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index fec0fc5..deae4c8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -200,7 +200,8 @@
*/
public class EventBus extends BroadcastReceiver {
- public static final String TAG = "EventBus";
+ private static final String TAG = "EventBus";
+ private static final boolean DEBUG_TRACE_ALL = false;
/**
* An event super class that allows us to track internal event state across subscriber
@@ -277,9 +278,6 @@
// The default priority of all subscribers
private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
- // Used for debugging everything
- private static final boolean DEBUG_TRACE_ALL = false;
-
// Orders the handlers by priority and registration time
private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
new file mode 100644
index 0000000..f7b2706
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 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.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the user taps on the Overview button to iterate to the next item in the
+ * Recents list.
+ */
+public class IterateRecentsEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
index 12e5d3d..968890a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
@@ -23,12 +23,12 @@
/**
* This is sent when a {@link TaskView} has been dismissed.
*/
-public class DismissTaskEvent extends EventBus.Event {
+public class DismissTaskViewEvent extends EventBus.Event {
public final Task task;
public final TaskView taskView;
- public DismissTaskEvent(Task task, TaskView taskView) {
+ public DismissTaskViewEvent(Task task, TaskView taskView) {
this.task = task;
this.taskView = taskView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
new file mode 100644
index 0000000..9f3e9d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Dismisses the currently focused task view.
+ */
+public class DismissFocusedTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
new file mode 100644
index 0000000..171ab5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Focuses the next task view in the stack.
+ */
+public class FocusNextTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
new file mode 100644
index 0000000..22469e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.events.ui.focus;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Focuses the previous task view in the stack.
+ */
+public class FocusPreviousTaskViewEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 735f79f..336d2db 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -19,8 +19,8 @@
import android.os.Handler;
/**
- * A dozer is a class that fires a trigger after it falls asleep. You can occasionally poke it to
- * wake it up, but it will fall asleep if left untouched.
+ * A dozer is a class that fires a trigger after it falls asleep.
+ * You can occasionally poke the trigger to wake it up, but it will fall asleep if left untouched.
*/
public class DozeTrigger {
@@ -28,7 +28,7 @@
boolean mIsDozing;
boolean mHasTriggered;
- int mDozeDurationSeconds;
+ int mDozeDurationMilliseconds;
Runnable mSleepRunnable;
// Sleep-runnable
@@ -41,9 +41,9 @@
}
};
- public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
+ public DozeTrigger(int dozeDurationMilliseconds, Runnable sleepRunnable) {
mHandler = new Handler();
- mDozeDurationSeconds = dozeDurationSeconds;
+ mDozeDurationMilliseconds = dozeDurationMilliseconds;
mSleepRunnable = sleepRunnable;
}
@@ -69,7 +69,7 @@
/** Poke this dozer to wake it up for a little bit. */
void forcePoke() {
mHandler.removeCallbacks(mDozeRunnable);
- mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
+ mHandler.postDelayed(mDozeRunnable, mDozeDurationMilliseconds);
mIsDozing = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 7f618e3..ad0d758 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -49,7 +49,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -69,6 +69,7 @@
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
private static final String TAG = "RecentsView";
+ private static final boolean DEBUG = false;
private static final boolean ADD_HEADER_BITMAP = true;
@@ -404,22 +405,6 @@
return super.verifyDrawable(who);
}
- /** Focuses the next task in the first stack view */
- public void focusNextTask(boolean forward) {
- // Get the first stack view
- if (mTaskStackView != null) {
- mTaskStackView.focusNextTask(forward, true);
- }
- }
-
- /** Dismisses the focused task. */
- public void dismissFocusedTask() {
- // Get the first stack view
- if (mTaskStackView != null) {
- mTaskStackView.dismissFocusedTask();
- }
- }
-
/** Unfilters any filtered stacks */
public boolean unfilterFilteredStacks() {
if (mStacks != null) {
@@ -562,7 +547,7 @@
// Disable any focused state before we draw the header
// Upfront the processing of the thumbnail
if (tv.isFocusedTask()) {
- tv.unsetFocusedTask();
+ tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
}
TaskViewTransform transform = new TaskViewTransform();
transform = stackView.getStackAlgorithm().getStackTransform(tv.mTask, stackScroll,
@@ -682,7 +667,7 @@
} else {
// Dismiss the task and return the user to home if we fail to
// launch the task
- EventBus.getDefault().send(new DismissTaskEvent(task, tv));
+ EventBus.getDefault().send(new DismissTaskViewEvent(task, tv));
if (mCb != null) {
mCb.onTaskLaunchFailed();
}
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 5928854..9ef3733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,6 +23,8 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.SystemService;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -30,6 +32,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
@@ -37,8 +40,11 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -60,6 +66,9 @@
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
ViewPool.ViewPoolConsumer<TaskView, Task> {
+ private final static String TAG = "TaskStackView";
+ private final static boolean DEBUG = false;
+
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
@@ -80,14 +89,12 @@
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
DozeTrigger mUIDozeTrigger;
int mFocusedTaskIndex = -1;
- int mPrevAccessibilityFocusedIndex = -1;
// Optimizations
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mStackViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
- boolean mStartEnterAnimationCompleted;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
Rect mTaskStackBounds = new Rect();
@@ -219,7 +226,6 @@
mStackViewsDirty = true;
mStackViewsClipDirty = true;
mAwaitingFirstLayout = true;
- mPrevAccessibilityFocusedIndex = -1;
if (mUIDozeTrigger != null) {
mUIDozeTrigger.stopDozing();
mUIDozeTrigger.resetTrigger();
@@ -332,8 +338,6 @@
/** Synchronizes the views with the model */
boolean synchronizeStackViewsWithModel() {
if (mStackViewsDirty) {
- SystemServicesProxy ssp = Recents.getSystemServices();
-
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
float stackScroll = mStackScroller.getStackScroll();
@@ -344,8 +348,9 @@
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
List<TaskView> taskViews = getTaskViews();
+ boolean wasLastFocusedTaskAnimated = false;
+ int lastFocusedTaskIndex = -1;
int taskViewCount = taskViews.size();
- boolean reaquireAccessibilityFocus = false;
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
@@ -353,8 +358,12 @@
if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
mTmpTaskViewMap.put(task, tv);
} else {
+ if (tv.isFocusedTask()) {
+ wasLastFocusedTaskAnimated = tv.isFocusAnimated();
+ lastFocusedTaskIndex = taskIndex;
+ resetFocusedTask();
+ }
mViewPool.returnViewToPool(tv);
- reaquireAccessibilityFocus |= (i == mPrevAccessibilityFocusedIndex);
}
}
@@ -385,21 +394,14 @@
// Animate the task into place
tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
+ }
- // Request accessibility focus on the next view if we removed the task
- // that previously held accessibility focus
- if (reaquireAccessibilityFocus) {
- taskViews = getTaskViews();
- taskViewCount = taskViews.size();
- if (taskViewCount > 0 && ssp.isTouchExplorationEnabled() &&
- mPrevAccessibilityFocusedIndex != -1) {
- TaskView atv = taskViews.get(taskViewCount - 1);
- int indexOfTask = mStack.indexOfTask(atv.getTask());
- if (mPrevAccessibilityFocusedIndex != indexOfTask) {
- tv.requestAccessibilityFocus();
- mPrevAccessibilityFocusedIndex = indexOfTask;
- }
- }
+ // Update the focus if the previous focused task was returned to the view pool
+ if (lastFocusedTaskIndex != -1) {
+ if (lastFocusedTaskIndex < visibleRange[1]) {
+ setFocusedTask(visibleRange[1], false, wasLastFocusedTaskAnimated);
+ } else {
+ setFocusedTask(visibleRange[0], false, wasLastFocusedTaskAnimated);
}
}
@@ -473,118 +475,80 @@
return mStackScroller;
}
- /** Focuses the task at the specified index in the stack */
- void focusTask(int taskIndex, boolean scrollToNewPosition, final boolean animateFocusedState) {
- // Return early if the task is already focused
- if (taskIndex == mFocusedTaskIndex) return;
+ /**
+ * Sets the focused task to the provided (bounded taskIndex).
+ */
+ private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
+ setFocusedTask(taskIndex, scrollToTask, animated, true);
+ }
- if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
- mFocusedTaskIndex = taskIndex;
- mPrevAccessibilityFocusedIndex = taskIndex;
+ /**
+ * Sets the focused task to the provided (bounded taskIndex).
+ */
+ private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
+ final boolean requestViewFocus) {
+ // Find the next task to focus
+ int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
+ Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
+ final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
+ mStack.getTasks().get(newFocusedTaskIndex) : null;
- // Focus the view if possible, otherwise, focus the view after we scroll into position
- final Task t = mStack.getTasks().get(mFocusedTaskIndex);
- Runnable postScrollRunnable = new Runnable() {
+ // Reset the last focused task state if changed
+ if (mFocusedTaskIndex != -1) {
+ Task focusedTask = mStack.getTasks().get(mFocusedTaskIndex);
+ if (focusedTask != newFocusedTask) {
+ resetFocusedTask();
+ }
+ }
+
+ mFocusedTaskIndex = newFocusedTaskIndex;
+ if (mFocusedTaskIndex != -1) {
+ Runnable focusTaskRunnable = new Runnable() {
@Override
public void run() {
- TaskView tv = getChildViewForTask(t);
+ TaskView tv = getChildViewForTask(newFocusedTask);
if (tv != null) {
- tv.setFocusedTask(animateFocusedState);
- tv.requestAccessibilityFocus();
+ tv.setFocusedState(true, animated, requestViewFocus);
}
}
};
- // Scroll the view into position (just center it in the curve)
- if (scrollToNewPosition) {
- float newScroll = mLayoutAlgorithm.getStackScrollForTask(t) - 0.5f;
+ if (scrollToTask) {
+ // TODO: Center the newly focused task view
+ float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask) - 0.5f;
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
- mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
+ focusTaskRunnable);
} else {
- if (postScrollRunnable != null) {
- postScrollRunnable.run();
- }
+ focusTaskRunnable.run();
}
-
}
}
/**
- * Ensures that there is a task focused, if nothing is focused, then we will use the task
- * at the center of the visible stack.
+ * Sets the focused task relative to the currently focused task.
+ *
+ * @param animated determines whether to actually draw the highlight along with the change in
+ * focus.
*/
- public boolean ensureFocusedTask(boolean findClosestToCenter) {
- if (mFocusedTaskIndex < 0) {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (findClosestToCenter) {
- // If there is no task focused, then find the task that is closes to the center
- // of the screen and use that as the currently focused task
- int x = mLayoutAlgorithm.mStackRect.centerX();
- int y = mLayoutAlgorithm.mStackRect.centerY();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- tv.getHitRect(mTmpRect);
- if (mTmpRect.contains(x, y)) {
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- mPrevAccessibilityFocusedIndex = mFocusedTaskIndex;
- break;
- }
- }
- }
- // If we can't find the center task, then use the front most index
- if (mFocusedTaskIndex < 0 && taskViewCount > 0) {
- TaskView tv = taskViews.get(taskViewCount - 1);
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- mPrevAccessibilityFocusedIndex = mFocusedTaskIndex;
- }
- }
- return mFocusedTaskIndex >= 0;
- }
-
- /**
- * Focuses the next task in the stack.
- * @param animateFocusedState determines whether to actually draw the highlight along with
- * the change in focus, as well as whether to scroll to fit the
- * task into view.
- */
- public void focusNextTask(boolean forward, boolean animateFocusedState) {
+ public void setRelativeFocusedTask(boolean forward, boolean animated) {
// Find the next index to focus
- int numTasks = mStack.getTaskCount();
- if (numTasks == 0) return;
-
- int direction = (forward ? -1 : 1);
- int newIndex = mFocusedTaskIndex + direction;
- if (newIndex >= 0 && newIndex <= (numTasks - 1)) {
- newIndex = Math.max(0, Math.min(numTasks - 1, newIndex));
- focusTask(newIndex, true, animateFocusedState);
- }
+ int newIndex = mFocusedTaskIndex + (forward ? -1 : 1);
+ setFocusedTask(newIndex, true, animated);
}
- /** Dismisses the focused task. */
- public void dismissFocusedTask() {
- // Return early if the focused task index is invalid
- if (mFocusedTaskIndex < 0 || mFocusedTaskIndex >= mStack.getTaskCount()) {
- mFocusedTaskIndex = -1;
- return;
- }
-
- Task t = mStack.getTasks().get(mFocusedTaskIndex);
- TaskView tv = getChildViewForTask(t);
- tv.dismissTask();
- }
-
- /** Resets the focused task. */
+ /**
+ * Resets the focused task.
+ */
void resetFocusedTask() {
- if ((0 <= mFocusedTaskIndex) && (mFocusedTaskIndex < mStack.getTaskCount())) {
+ if (mFocusedTaskIndex != -1) {
Task t = mStack.getTasks().get(mFocusedTaskIndex);
TaskView tv = getChildViewForTask(t);
if (tv != null) {
- tv.unsetFocusedTask();
+ tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
}
}
mFocusedTaskIndex = -1;
- mPrevAccessibilityFocusedIndex = -1;
}
@Override
@@ -609,12 +573,12 @@
super.onInitializeAccessibilityNodeInfo(info);
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
- if (taskViewCount > 1 && mPrevAccessibilityFocusedIndex != -1) {
+ if (taskViewCount > 1 && mFocusedTaskIndex != -1) {
info.setScrollable(true);
- if (mPrevAccessibilityFocusedIndex > 0) {
+ if (mFocusedTaskIndex > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
- if (mPrevAccessibilityFocusedIndex < mStack.getTaskCount() - 1) {
+ if (mFocusedTaskIndex < mStack.getTaskCount() - 1) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
}
@@ -630,22 +594,14 @@
if (super.performAccessibilityAction(action, arguments)) {
return true;
}
- if (ensureFocusedTask(false)) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- if (mPrevAccessibilityFocusedIndex > 0) {
- focusNextTask(true, false);
- return true;
- }
- }
- break;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- if (mPrevAccessibilityFocusedIndex < mStack.getTaskCount() - 1) {
- focusNextTask(false, false);
- return true;
- }
- }
- break;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ setRelativeFocusedTask(true, false /* animated */);
+ return true;
+ }
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ setRelativeFocusedTask(false, false /* animated */);
+ return true;
}
}
return false;
@@ -678,7 +634,7 @@
/** Computes the stack and task rects */
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
- boolean launchedWithAltTab, boolean launchedFromHome) {
+ boolean launchedWithAltTab, boolean launchedFromHome) {
// Compute the rects in the stack algorithm
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
@@ -741,12 +697,12 @@
mTmpRect.setEmpty();
}
tv.measure(
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
- MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(
+ mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(
+ mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
+ MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -815,18 +771,12 @@
mStartEnterAnimationContext = null;
}
- // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
- // enter animation).
+ // Set the task focused state without requesting view focus, and leave the focus animations
+ // until after the enter-animation
RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- if (launchState.launchedWithAltTab) {
- if (launchState.launchedFromAppWithThumbnail) {
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
- launchState.launchedHasConfigurationChanged);
- } else {
- focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
- launchState.launchedHasConfigurationChanged);
- }
- }
+ int taskOffset = launchState.launchedFromHome ? -1 : -2;
+ setFocusedTask(mStack.getTaskCount() + taskOffset, false /* scrollToTask */,
+ false /* animated */, false /* requestViewFocus */);
// Start dozing
mUIDozeTrigger.startDozing();
@@ -874,32 +824,17 @@
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
- mStartEnterAnimationCompleted = true;
// Poke the dozer to restart the trigger after the animation completes
mUIDozeTrigger.poke();
- SystemServicesProxy ssp = Recents.getSystemServices();
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (taskViewCount > 0) {
- // Focus the first view if accessibility is enabled
- if (ssp.isTouchExplorationEnabled()) {
- TaskView tv = taskViews.get(taskViewCount - 1);
- tv.requestAccessibilityFocus();
- mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask());
- }
- }
-
- // Start the focus animation when alt-tabbing
- ArrayList<Task> tasks = mStack.getTasks();
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- if (launchState.launchedWithAltTab &&
- !launchState.launchedHasConfigurationChanged &&
- 0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) {
- TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex));
- if (tv != null) {
- tv.setFocusedTask(true);
- }
+ // Update the focused state here -- since we only set the focused task without
+ // requesting view focus in onFirstLayout(), actually request view focus and
+ // animate the focused state if we are alt-tabbing now, after the window enter
+ // animation is completed
+ if (mFocusedTaskIndex != -1) {
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ setFocusedTask(mFocusedTaskIndex, false /* scrollToTask */,
+ launchState.launchedWithAltTab);
}
}
});
@@ -1132,11 +1067,6 @@
public void prepareViewToEnterPool(TaskView tv) {
Task task = tv.getTask();
- // Clear the accessibility focus for that view
- if (tv.isAccessibilityFocused()) {
- tv.clearAccessibilityFocus();
- }
-
// Report that this tasks's data is no longer being used
Recents.getTaskLoader().unloadTaskData(task);
@@ -1167,11 +1097,6 @@
// If the doze trigger has already fired, then update the state for this task view
tv.setNoUserInteractionState();
- // If we've finished the start animation, then ensure we always enable the focus animations
- if (mStartEnterAnimationCompleted) {
- tv.enableFocusAnimations();
- }
-
// Find the index where this task should be placed in the stack
int insertIndex = -1;
int taskIndex = mStack.indexOfTask(task);
@@ -1231,13 +1156,6 @@
}
}
- @Override
- public void onTaskViewFocusChanged(TaskView tv, boolean focused) {
- if (focused) {
- mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
- }
- }
-
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
@@ -1259,13 +1177,13 @@
for (int i = tasks.size() - 1; i >= 0; i--) {
final Task t = tasks.get(i);
if (removedComponents.contains(t.key.getComponent())) {
- TaskView tv = getChildViewForTask(t);
+ final TaskView tv = getChildViewForTask(t);
if (tv != null) {
// For visible children, defer removing the task until after the animation
tv.startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
- mStack.removeTask(t);
+ removeTaskViewFromStack(tv);
}
}, 0);
} else {
@@ -1276,33 +1194,23 @@
}
}
- public final void onBusEvent(DismissTaskEvent event) {
- TaskView tv = event.taskView;
- Task task = tv.getTask();
- int taskIndex = mStack.indexOfTask(task);
- boolean taskWasFocused = tv.isFocusedTask();
+ public final void onBusEvent(DismissTaskViewEvent event) {
+ removeTaskViewFromStack(event.taskView);
+ }
- // Announce for accessibility
- tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
- tv.getTask().activityLabel));
+ public final void onBusEvent(FocusNextTaskViewEvent event) {
+ setRelativeFocusedTask(true, true);
+ }
- // Remove the task from the view
- mStack.removeTask(task);
+ public final void onBusEvent(FocusPreviousTaskViewEvent event) {
+ setRelativeFocusedTask(false, true);
+ }
- // If the dismissed task was focused, then we should focus the new task in the same index
- if (taskWasFocused) {
- ArrayList<Task> tasks = mStack.getTasks();
- int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
- if (nextTaskIndex >= 0) {
- Task nextTask = tasks.get(nextTaskIndex);
- TaskView nextTv = getChildViewForTask(nextTask);
- if (nextTv != null) {
- // Focus the next task, and only animate the visible state if we are launched
- // from Alt-Tab
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- nextTv.setFocusedTask(launchState.launchedWithAltTab);
- }
- }
+ public final void onBusEvent(DismissFocusedTaskViewEvent event) {
+ if (mFocusedTaskIndex != -1) {
+ Task t = mStack.getTasks().get(mFocusedTaskIndex);
+ TaskView tv = getChildViewForTask(t);
+ tv.dismissTask();
}
}
@@ -1316,4 +1224,32 @@
reset();
}
}
+
+ /**
+ * Removes the task from the stack, and updates the focus to the next task in the stack if the
+ * removed TaskView was focused.
+ */
+ private void removeTaskViewFromStack(TaskView tv) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Task task = tv.getTask();
+ int taskIndex = mStack.indexOfTask(task);
+ boolean taskWasFocused = tv.isFocusedTask();
+
+ // Reset the previously focused task before it is removed from the stack
+ resetFocusedTask();
+
+ // Announce for accessibility
+ tv.announceForAccessibility(getContext().getString(
+ R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
+
+ // Remove the task from the stack
+ mStack.removeTask(task);
+
+ if (taskWasFocused || ssp.isTouchExplorationEnabled()) {
+ // If the dismissed task was focused or if we are in touch exploration mode, then focus
+ // the next task
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ setFocusedTask(taskIndex - 1, true /* scrollToTask */, launchState.launchedWithAltTab);
+ }
+ }
}
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 1274318..3a1a987 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -28,7 +28,7 @@
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import java.util.List;
@@ -408,13 +408,9 @@
// Find the front most task and scroll the next task to the front
float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vScroll > 0) {
- if (mSv.ensureFocusedTask(true)) {
- mSv.focusNextTask(true, false);
- }
+ mSv.setRelativeFocusedTask(true, false /* animated */);
} else {
- if (mSv.ensureFocusedTask(true)) {
- mSv.focusNextTask(false, false);
- }
+ mSv.setRelativeFocusedTask(false, false /* animated */);
}
return true;
}
@@ -461,7 +457,7 @@
// Re-enable touch events from this task view
tv.setTouchEnabled(true);
// Remove the task view from the stack
- EventBus.getDefault().send(new DismissTaskEvent(tv.getTask(), tv));
+ EventBus.getDefault().send(new DismissTaskViewEvent(tv.getTask(), tv));
// Keep track of deletions by keyboard
MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceSwipeGesture);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index bab4da7..0a5ee79 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -29,8 +29,8 @@
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -40,13 +40,15 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -55,11 +57,13 @@
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View.OnClickListener, View.OnLongClickListener {
+ private final static String TAG = "TaskView";
+ private final static boolean DEBUG = false;
+
/** The TaskView callbacks */
interface TaskViewCallbacks {
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
public void onTaskViewClipStateChanged(TaskView tv);
- public void onTaskViewFocusChanged(TaskView tv, boolean focused);
}
RecentsConfiguration mConfig;
@@ -76,6 +80,7 @@
Task mTask;
boolean mTaskDataLoaded;
boolean mIsFocused;
+ boolean mIsFocusAnimated;
boolean mFocusAnimationsEnabled;
boolean mClipViewInStack;
AnimateableViewBounds mViewBounds;
@@ -397,15 +402,6 @@
ctx.postAnimationTrigger.increment();
startDelay = delay;
}
-
- // Enable the focus animations from this point onwards so that they aren't affected by the
- // window transitions
- postDelayed(new Runnable() {
- @Override
- public void run() {
- enableFocusAnimations();
- }
- }, startDelay);
}
public void fadeInActionButton(int delay, int duration) {
@@ -547,7 +543,7 @@
startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
- EventBus.getDefault().send(new DismissTaskEvent(mTask, tv));
+ EventBus.getDefault().send(new DismissTaskViewEvent(mTask, tv));
}
}, 0);
}
@@ -620,6 +616,8 @@
anim.addListener(postAnimRunnable);
}
anim.start();
+ } else {
+ postAnimRunnable.onAnimationEnd(null);
}
}
@@ -641,58 +639,32 @@
/**** View focus state ****/
/**
- * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
- * if the view is not currently visible, or we are in touch state (where we still want to keep
- * track of focus).
+ * Explicitly sets the focused state of this task.
*/
- public void setFocusedTask(boolean animateFocusedState) {
- mIsFocused = true;
- if (mFocusAnimationsEnabled) {
- // Focus the header bar
- mHeaderView.onTaskViewFocusChanged(true, animateFocusedState);
- }
- // Update the thumbnail alpha with the focus
- mThumbnailView.onFocusChanged(true);
- // Call the callback
- if (mCb != null) {
- mCb.onTaskViewFocusChanged(this, true);
- }
- // Workaround, we don't always want it focusable in touch mode, but we want the first task
- // to be focused after the enter-recents animation, which can be triggered from either touch
- // or keyboard
- setFocusableInTouchMode(true);
- requestFocus();
- setFocusableInTouchMode(false);
- invalidate();
- }
-
- /**
- * Unsets the focused task explicitly.
- */
- void unsetFocusedTask() {
- mIsFocused = false;
- if (mFocusAnimationsEnabled) {
- // Un-focus the header bar
- mHeaderView.onTaskViewFocusChanged(false, true);
+ public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
+ if (DEBUG) {
+ Log.d(TAG, "setFocusedState: " + mTask.activityLabel + " focused: " + isFocused +
+ " mIsFocused: " + mIsFocused + " animated: " + animated +
+ " requestViewFocus: " + requestViewFocus + " isFocused(): " + isFocused() +
+ " isAccessibilityFocused(): " + isAccessibilityFocused());
}
- // Update the thumbnail alpha with the focus
- mThumbnailView.onFocusChanged(false);
- // Call the callback
- if (mCb != null) {
- mCb.onTaskViewFocusChanged(this, false);
- }
- invalidate();
- }
-
- /**
- * Updates the explicitly focused state when the view focus changes.
- */
- @Override
- protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- if (!gainFocus) {
- unsetFocusedTask();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mIsFocused = isFocused;
+ mIsFocusAnimated = animated;
+ mHeaderView.onTaskViewFocusChanged(isFocused, animated);
+ mThumbnailView.onFocusChanged(isFocused);
+ if (isFocused) {
+ if (requestViewFocus && !isFocused()) {
+ requestFocus();
+ }
+ if (requestViewFocus && !isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
+ requestAccessibilityFocus();
+ }
+ } else {
+ if (isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
+ clearAccessibilityFocus();
+ }
}
}
@@ -700,17 +672,14 @@
* Returns whether we have explicitly been focused.
*/
public boolean isFocusedTask() {
- return mIsFocused || isFocused();
+ return mIsFocused;
}
- /** Enables all focus animations. */
- void enableFocusAnimations() {
- boolean wasFocusAnimationsEnabled = mFocusAnimationsEnabled;
- mFocusAnimationsEnabled = true;
- if (mIsFocused && !wasFocusAnimationsEnabled) {
- // Re-notify the header if we were focused and animations were not previously enabled
- mHeaderView.onTaskViewFocusChanged(true, true);
- }
+ /**
+ * Returns whether this focused task is animated.
+ */
+ public boolean isFocusAnimated() {
+ return mIsFocusAnimated;
}
public void disableLayersForOneFrame() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index e1e07ef..f6353f8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,7 +39,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -244,9 +243,8 @@
mMoveTaskButton.setOnClickListener(this);
// In accessibility, a single click on the focused app info button will show it
- AccessibilityManager am = (AccessibilityManager) getContext().
- getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (am != null && am.isEnabled()) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isTouchExplorationEnabled()) {
mApplicationIcon.setOnClickListener(this);
}
}
@@ -369,9 +367,6 @@
/** Notifies the associated TaskView has been focused. */
void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
- // If we are not animating the visible state, just return
- if (!animateFocusedState) return;
-
boolean isRunning = false;
if (mFocusAnimator != null) {
isRunning = mFocusAnimator.isRunning();
@@ -379,6 +374,9 @@
}
if (focused) {
+ // If we are not animating the visible state, just return
+ if (!animateFocusedState) return;
+
int currentColor = mBackgroundColor;
int secondaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
int[][] states = new int[][] {