Add dismiss animation for Recents TV and Enable Dismiss
Redo rect calculations for new positions.
Refactor some code to make a RecentsTvImpl.
Change-Id: Ifa269fe18ea40ea9a102ec0207a6c7ab796e6f77
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..73f5116
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..787e259
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..6ebbc83
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 567e009..a00c744 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -28,12 +28,8 @@
android:clipChildren="false"
android:clipToPadding="false"
android:descendantFocusability="beforeDescendants"
- android:layout_gravity="center"
- android:gravity="center"
- android:paddingStart="@dimen/recents_tv_grid_row_padding"
- android:paddingEnd="@dimen/recents_tv_grid_row_padding"
+ android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
android:focusable="true" />
-
<View
android:id="@+id/pip_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
index 54e97da..766ef60 100644
--- a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
@@ -21,9 +21,11 @@
android:focusableInTouchMode="true"
android:layout_gravity="center"
android:layout_centerInParent="true"
+ android:orientation="vertical"
android:layoutDirection="ltr">
<LinearLayout
+ android:id="@+id/recents_tv_card"
android:layout_width="@dimen/recents_tv_card_width"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
@@ -66,4 +68,30 @@
android:layout_centerHorizontal="true"
android:layout_below="@id/card_title_text" />
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/card_dismiss"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal"
+ android:layout_below="@id/recents_tv_card"
+ android:alpha="0.0">
+ <ImageView
+ android:id="@+id/card_dismiss_icon"
+ android:layout_width="@dimen/recents_tv_dismiss_icon_size"
+ android:layout_height="@dimen/recents_tv_dismiss_icon_size"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
+ android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
+ android:src="@drawable/ic_cancel_white_24dp" />
+ <TextView
+ android:id="@+id/card_dismiss_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/recents_tv_dismiss_text_size"
+ android:fontFamily="@string/font_roboto_light"
+ android:textColor="@color/recents_tv_dismiss_text_color"
+ android:text="@string/recents_tv_dismiss"
+ android:layout_gravity="center_horizontal" />
+ </LinearLayout>
</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index af99aae..4126d3c 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -19,4 +19,5 @@
<resources>
<color name="recents_tv_card_background_color">#FF37474F</color>
<color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
+ <color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 6b153d1..953dd65 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -18,8 +18,8 @@
-->
<resources>
<!-- Dimens for recents card in the recents view on tv -->
- <dimen name="recents_tv_card_width">268dip</dimen>
- <dimen name="recents_tv_screenshot_height">151dip</dimen>
+ <dimen name="recents_tv_card_width">240dip</dimen>
+ <dimen name="recents_tv_screenshot_height">135dip</dimen>
<dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
<dimen name="recents_tv_banner_width">114dip</dimen>
<dimen name="recents_tv_banner_height">64dip</dimen>
@@ -29,10 +29,10 @@
<dimen name="recents_tv_text_padding_bottom">12dip</dimen>
<!-- Padding for grid view in recents view on tv -->
- <dimen name="recents_tv_grid_row_padding">56dip</dimen>
- <dimen name="recents_tv_gird_row_top_padding">57dip</dimen>
+ <dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
<dimen name="recents_tv_grid_max_row_height">268dip</dimen>
- <dimen name="recents_tv_gird_card_spacing">20dip</dimen>
+ <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
+ <dimen name="recents_tv_gird_focused_card_delta">44dip</dimen>
<!-- Values for focus animation -->
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
@@ -43,4 +43,13 @@
<!-- Values for text on recents cards on tv -->
<dimen name="recents_tv_title_text_size">12sp</dimen>
+
+ <!-- Values for card dismiss state -->
+ <dimen name="recents_tv_dismiss_shift_down">48dip</dimen>
+ <dimen name="recents_tv_dismiss_top_margin">356dip</dimen>
+ <dimen name="recents_tv_dismiss_icon_size">24dip</dimen>
+ <dimen name="recents_tv_dismiss_icon_top_margin">38dip</dimen>
+ <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
+ <dimen name="recents_tv_dismiss_text_size">12sp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index bfd8f8b..c60c245 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -15,4 +15,6 @@
-->
<resources>
<integer name="item_scale_anim_duration">150</integer>
+ <integer name="dismiss_short_duration">200</integer>
+ <integer name="dismiss_long_duration">400</integer>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 0e1fe8f..da6c279 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -35,7 +35,11 @@
<string name="pip_onboarding_description">Press and hold the HOME button to control PIP</string>
<!-- Button to close picture-in-picture (PIP) onboarding screen. -->
<string name="pip_onboarding_button">Got it</string>
+ <!-- Dismiss icon description -->
+ <string name="recents_tv_dismiss">Dismiss</string>
<!-- Font for Recents -->
<!-- DO NOT TRANSLATE -->
<string name="font_roboto_regular" translatable="false">sans-serif</string>
+ <!-- DO NOT TRANSLATE -->
+ <string name="font_roboto_light" translatable="false">sans-serif-light</string>
</resources>
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index 6a72e54..bd72c51 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item format="float" type="integer" name="unselected_scale">1.0</item>
- <item format="float" type="integer" name="selected_scale">1.1</item>
+ <item format="float" type="integer" name="selected_scale">1.259</item>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 2b6ed44..da07aec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents;
import android.app.ActivityManager;
+import android.app.UiModeManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -53,6 +54,7 @@
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.tv.RecentsTvImpl;
import java.util.ArrayList;
@@ -182,7 +184,13 @@
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
- mImpl = new RecentsImpl(mContext);
+ UiModeManager uiModeManager = (UiModeManager) mContext.
+ getSystemService(Context.UI_MODE_SERVICE);
+ if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+ mImpl = new RecentsTvImpl(mContext);
+ } else {
+ mImpl = new RecentsImpl(mContext);
+ }
// Check if there is a recents override package
if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9be24de..880fe10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -21,12 +21,10 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
-import android.app.UiModeManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -66,7 +64,6 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.tv.views.TaskCardView;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskStackViewScroller;
@@ -96,10 +93,6 @@
public final static String RECENTS_PACKAGE = "com.android.systemui";
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
- public final static String RECENTS_TV_ACTIVITY = "com.android.systemui.recents.tv.RecentsTvActivity";
-
- //Used to store tv or non-tv activty for use in creating intents.
- private final String mRecentsIntentActivityName;
/**
* An implementation of ITaskStackListener, that allows us to listen for changes to the system
@@ -158,16 +151,15 @@
}
}
- private static RecentsTaskLoadPlan sInstanceLoadPlan;
+ protected static RecentsTaskLoadPlan sInstanceLoadPlan;
- Context mContext;
- Handler mHandler;
+ protected Context mContext;
+ protected Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
RecentsAppWidgetHost mAppWidgetHost;
- boolean mCanReuseTaskStackViews = true;
+ protected boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mLaunchedWhileDocking;
- private boolean mIsRunningOnTv;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -182,11 +174,11 @@
// Header (for transition)
TaskViewHeader mHeaderBar;
final Object mHeaderBarLock = new Object();
- TaskStackView mDummyStackView;
+ protected TaskStackView mDummyStackView;
// Variables to keep track of if we need to start recents after binding
- boolean mTriggeredFromAltTab;
- long mLastToggleTime;
+ protected boolean mTriggeredFromAltTab;
+ protected long mLastToggleTime;
DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
@Override
public void run() {
@@ -197,7 +189,7 @@
}
});
- Bitmap mThumbnailTransitionBitmapCache;
+ protected Bitmap mThumbnailTransitionBitmapCache;
Task mThumbnailTransitionBitmapCacheKey;
public RecentsImpl(Context context) {
@@ -227,16 +219,6 @@
launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
launchOpts.onlyLoadForCache = true;
loader.loadTasks(mContext, plan, launchOpts);
-
- //Manager used to determine if we are running on tv or not
- UiModeManager uiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
- if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
- mRecentsIntentActivityName = RECENTS_TV_ACTIVITY;
- mIsRunningOnTv = true;
- } else {
- mRecentsIntentActivityName = RECENTS_ACTIVITY;
- mIsRunningOnTv = false;
- }
}
public void onBootCompleted() {
@@ -729,7 +711,7 @@
/**
* Creates the activity options for a unknown state->recents transition.
*/
- private ActivityOptions getUnknownTransitionActivityOptions() {
+ protected ActivityOptions getUnknownTransitionActivityOptions() {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit,
@@ -739,7 +721,7 @@
/**
* Creates the activity options for a home->recents transition.
*/
- private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+ protected ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
if (fromSearchHome) {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_search_launcher_enter,
@@ -797,22 +779,6 @@
}
}
- /**
- * Creates the activity options for an app->recents transition on TV.
- */
- private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
- ActivityManager.RunningTaskInfo topTask) {
- Bitmap thumbnail = mThumbnailTransitionBitmapCache;
- Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
- if (thumbnail != null) {
- return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- null, (int) rect.left, (int) rect.top,
- (int) rect.width(), (int) rect.height(), mHandler, null);
- }
- // If both the screenshot and thumbnail fails, then just fall back to the default transition
- return getUnknownTransitionActivityOptions();
- }
-
private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
TaskViewTransform toTransform) {
Bitmap thumbnail;
@@ -888,15 +854,10 @@
/**
* Shows the recents activity
*/
- private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
boolean isTopTaskHome, boolean animate) {
RecentsTaskLoader loader = Recents.getTaskLoader();
- // If we are on TV, divert to a different helper method
- if (mIsRunningOnTv) {
- setUpAndStartTvRecents(topTask, isTopTaskHome, animate);
- return;
- }
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
@@ -972,90 +933,6 @@
}
/**
- * Used to set up the animations of Tv Recents, then start the Recents Activity.
- * TODO: Add the Transitions for Home -> Recents TV
- * TODO: Shift Transition code to separate class under /tv directory and access
- * from here
- */
- private void setUpAndStartTvRecents(ActivityManager.RunningTaskInfo topTask,
- boolean isTopTaskHome, boolean animate) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
-
- // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
- // should always preload the tasks now. If we are dragging in recents, reload them as
- // the stacks might have changed.
- if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
- // Create a new load plan if preloadRecents() was never triggered
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- }
- if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
- }
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
- // Update the header bar if necessary
- updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
-
- // Prepare the dummy stack for the transition
- TaskStackLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
-
- if (!animate) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
- return;
- }
-
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
- if (useThumbnailTransition) {
- // Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
- if (opts != null) {
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
- } else {
- // Fall through below to the non-thumbnail transition
- useThumbnailTransition = false;
- }
- }
-
- if (!useThumbnailTransition) {
- // If there is no thumbnail transition, but is launching from home into recents, then
- // use a quick home transition and do the animation from home
- if (hasRecentTasks) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- String homeActivityPackage = ssp.getHomeActivityPackageName();
- String searchWidgetPackage = null;
- if (RecentsDebugFlags.Static.EnableSearchBar) {
- searchWidgetPackage = Prefs.getString(mContext,
- Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
- } else {
- AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
- if (searchWidgetInfo != null) {
- searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
- }
- }
-
- // Determine whether we are coming from a search owned home activity
- boolean fromSearchHome = (homeActivityPackage != null) &&
- homeActivityPackage.equals(searchWidgetPackage);
- ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
- startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
- false /* fromThumbnail */, stackVr);
- } else {
- // Otherwise we do the normal fade from an unknown source
- ActivityOptions opts = getUnknownTransitionActivityOptions();
- startRecentsActivity(topTask, opts, true /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
- }
- }
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
-
- /**
* Starts the recents activity.
*/
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
@@ -1078,7 +955,7 @@
launchState.launchedWhileDocking = mLaunchedWhileDocking;
Intent intent = new Intent();
- intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
+ intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 532e796..f7807e5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -47,7 +47,6 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -73,6 +72,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
+import com.android.systemui.recents.tv.RecentsTvImpl;
import java.io.IOException;
import java.util.ArrayList;
@@ -280,7 +280,7 @@
// Check if the front most activity is recents
if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
(topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) ||
- topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) {
+ topActivity.getClassName().equals(RecentsTvImpl.RECENTS_TV_ACTIVITY)))) {
if (isHomeTopMost != null) {
isHomeTopMost.value = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
new file mode 100644
index 0000000..9fd5d55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -0,0 +1,139 @@
+/*
+ * 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.systemui.recents.tv;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import com.android.systemui.recents.*;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+public class RecentsTvImpl extends RecentsImpl{
+ public final static String RECENTS_TV_ACTIVITY =
+ "com.android.systemui.recents.tv.RecentsTvActivity";
+
+ public RecentsTvImpl(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ boolean isTopTaskHome, boolean animate) {
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+
+ // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+ // should always preload the tasks now. If we are dragging in recents, reload them as
+ // the stacks might have changed.
+ if (mTriggeredFromAltTab || sInstanceLoadPlan == null) {
+ // Create a new load plan if preloadRecents() was never triggered
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ }
+ if (mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+ loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
+ }
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+ if (!animate) {
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
+ startRecentsActivity(topTask, opts, false /* fromHome */, false /* fromThumbnail*/);
+ return;
+ }
+
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+ if (useThumbnailTransition) {
+ // Try starting with a thumbnail transition
+ ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
+ if (opts != null) {
+ startRecentsActivity(topTask, opts, false /* fromHome */, true /* fromThumbnail */);
+ } else {
+ // Fall through below to the non-thumbnail transition
+ useThumbnailTransition = false;
+ }
+ }
+
+ if (!useThumbnailTransition) {
+ // If there is no thumbnail transition, but is launching from home into recents, then
+ // use a quick home transition and do the animation from home
+ if (hasRecentTasks) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityOptions opts = getHomeTransitionActivityOptions(false);
+ startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
+ } else {
+ // Otherwise we do the normal fade from an unknown source
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
+ }
+ }
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+
+ protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, boolean fromHome, boolean fromThumbnail) {
+ // Update the configuration based on the launch options
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ launchState.launchedFromHome = fromHome;
+ launchState.launchedFromSearchHome = false;
+ launchState.launchedFromApp = fromThumbnail;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+ launchState.launchedHasConfigurationChanged = false;
+
+ Intent intent = new Intent();
+ intent.setClassName(RECENTS_PACKAGE, RECENTS_TV_ACTIVITY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+
+ if (opts != null) {
+ mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
+ } else {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+ mCanReuseTaskStackViews = true;
+ EventBus.getDefault().send(new RecentsActivityStartingEvent());
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition on TV.
+ */
+ private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
+ ActivityManager.RunningTaskInfo topTask) {
+ Bitmap thumbnail = mThumbnailTransitionBitmapCache;
+ Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
+ if (thumbnail != null) {
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ null, (int) rect.left, (int) rect.top,
+ (int) rect.width(), (int) rect.height(), mHandler, null);
+ }
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
new file mode 100644
index 0000000..8996d0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
@@ -0,0 +1,80 @@
+/*
+ * 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.systemui.recents.tv.animations;
+
+
+import android.animation.Animator;
+import android.content.res.Resources;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.View;
+import android.widget.LinearLayout;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+import com.android.systemui.R;
+
+public class DismissAnimationsHolder {
+ private LinearLayout mDismissArea;
+ private LinearLayout mTaskCardView;
+ private FastOutSlowInInterpolator mFastOutSlowIn;
+ private int mCardYDelta;
+ private long mShortDuration;
+ private long mLongDuration;
+
+ public DismissAnimationsHolder(TaskCardView taskCardView) {
+ mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
+ mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
+ mFastOutSlowIn = new FastOutSlowInInterpolator();
+
+ Resources res = taskCardView.getResources();
+ mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
+ mShortDuration = res.getInteger(R.integer.dismiss_short_duration);
+ mLongDuration = res.getInteger(R.integer.dismiss_long_duration);
+ }
+
+ public void startEnterAnimation() {
+ mDismissArea.animate().setDuration(mShortDuration);
+ mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+ mDismissArea.animate().alpha(1.0f);
+
+ mTaskCardView.animate().setDuration(mShortDuration);
+ mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+ mTaskCardView.animate().translationYBy(mCardYDelta);
+ mTaskCardView.animate().alpha(0.5f);
+ }
+
+ public void startExitAnimation() {
+ mDismissArea.animate().setDuration(mShortDuration);
+ mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+ mDismissArea.animate().alpha(0.0f);
+
+ mTaskCardView.animate().setDuration(mShortDuration);
+ mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+ mTaskCardView.animate().translationYBy(-mCardYDelta);
+ mTaskCardView.animate().alpha(1.0f);
+ }
+
+ public void startDismissAnimation(Animator.AnimatorListener listener) {
+ mDismissArea.animate().setDuration(mShortDuration);
+ mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+ mDismissArea.animate().alpha(0.0f);
+
+ mTaskCardView.animate().setDuration(mLongDuration);
+ mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+ mTaskCardView.animate().translationYBy(mCardYDelta);
+ mTaskCardView.animate().alpha(0.0f);
+ mTaskCardView.animate().setListener(listener);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index 365b29d..888561c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -33,6 +33,8 @@
private final float mSelectedScaleDelta;
private final float mUnselectedZ;
private final float mSelectedZDelta;
+ private final float mUnselectedSpacing;
+ private final float mSelectedSpacingDelta;
private final int mAnimDuration;
private final Interpolator mFocusInterpolator;
@@ -57,6 +59,9 @@
mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
+ mUnselectedSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing);
+ mSelectedSpacingDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
+
mAnimDuration = res.getInteger(R.integer.item_scale_anim_duration);
mFocusInterpolator = new AccelerateDecelerateInterpolator();
@@ -85,10 +90,14 @@
float scale = mUnselectedScale + (level * mSelectedScaleDelta);
float z = mUnselectedZ + (level * mSelectedZDelta);
+ float spacing = mUnselectedSpacing + (level * mSelectedSpacingDelta);
mTargetView.setScaleX(scale);
mTargetView.setScaleY(scale);
mTargetView.setZ(z);
+
+ mTargetView.setPadding((int) spacing, mTargetView.getPaddingTop(),
+ (int) spacing, mTargetView.getPaddingBottom());
}
public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 5775b60..3343aec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.recents.tv.views;
+import android.animation.Animator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -22,12 +23,14 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Display;
+import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
import com.android.systemui.recents.model.Task;
@@ -37,8 +40,10 @@
private TextView mTitleTextView;
private ImageView mBadgeView;
private Task mTask;
+ private boolean mDismissState;
private ViewFocusAnimator mViewFocusAnimator;
+ private DismissAnimationsHolder mDismissAnimationsHolder;
public TaskCardView(Context context) {
this(context, null);
@@ -51,6 +56,7 @@
public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewFocusAnimator = new ViewFocusAnimator(this);
+ mDismissState = false;
}
@Override
@@ -58,6 +64,7 @@
mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
+ mDismissAnimationsHolder = new DismissAnimationsHolder(this);
}
public void init(Task task) {
@@ -98,13 +105,23 @@
int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
int widthDelta = (int) (width * scale - width);
- int height = (int) (res.getDimensionPixelOffset(
- R.dimen.recents_tv_screenshot_height) * scale);
- int padding = res.getDimensionPixelOffset(R.dimen.recents_tv_grid_row_padding);
+ int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
+ int heightDelta = (int) (height * scale - height);
+ int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
- int headerHeight = (int) ((res.getDimensionPixelOffset(
- R.dimen.recents_tv_card_extra_badge_size) +
- res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom)) * scale);
+ int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
+ int headerHeightDelta = (int) (headerHeight * scale - headerHeight);
+
+ int dismissAreaHeight =
+ res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_top_margin) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_bottom_margin) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_size) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_text_size);
+
+ int dismissAreaHeightDelta = (int) (dismissAreaHeight * scale - dismissAreaHeight);
+
+ int totalHeightDelta = heightDelta + headerHeightDelta + dismissAreaHeightDelta;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
@@ -113,9 +130,72 @@
int screenWidth = size.x;
int screenHeight = size.y;
- return new Rect(screenWidth - width - padding - widthDelta / 2,
- screenHeight / 2 - height / 2 + headerHeight / 2,
- screenWidth - padding + widthDelta / 2,
- screenHeight / 2 + height / 2 + headerHeight / 2);
+ return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
+ topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
+ screenWidth / 2 + width / 2 + widthDelta / 2,
+ topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale) +
+ (int) (height * scale));
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_DOWN : {
+ if (!isInDismissState()) {
+ setDismissState(true);
+ return true;
+ }
+ break;
+ }
+ case KeyEvent.KEYCODE_DPAD_UP : {
+ if (isInDismissState()) {
+ setDismissState(false);
+ return true;
+ }
+ break;
+ }
+
+ //Eat right and left key presses when we are in dismiss state
+ case KeyEvent.KEYCODE_DPAD_LEFT : {
+ if (isInDismissState()) {
+ return true;
+ }
+ break;
+ }
+ case KeyEvent.KEYCODE_DPAD_RIGHT : {
+ if (isInDismissState()) {
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void setDismissState(boolean dismissState) {
+ if (mDismissState != dismissState) {
+ mDismissState = dismissState;
+ if (dismissState) {
+ mDismissAnimationsHolder.startEnterAnimation();
+ } else {
+ mDismissAnimationsHolder.startExitAnimation();
+ }
+ }
+ }
+
+ public boolean isInDismissState() {
+ return mDismissState;
+ }
+
+ public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
+ mDismissAnimationsHolder.startDismissAnimation(listener);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ setDismissState(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 4458639..29a72da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -41,7 +41,6 @@
private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
-
public TaskStackHorizontalGridView(Context context) {
this(context, null);
}
@@ -53,7 +52,7 @@
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- setItemMargin((int) getResources().getDimension(R.dimen.recents_tv_gird_card_spacing));
+ setWindowAlignment(WINDOW_ALIGN_NO_EDGE);
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
super.onAttachedToWindow();
}
@@ -109,6 +108,13 @@
}
/**
+ * @return - The focused task card view.
+ */
+ public TaskCardView getFocusedTaskCardView() {
+ return ((TaskCardView)findFocus());
+ }
+
+ /**
* @param task
* @return Child view for given task
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index fba424e..3788719 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.recents.tv.views;
+import android.animation.Animator;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -25,6 +26,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
@@ -39,7 +41,7 @@
private static final String TAG = "TaskStackViewAdapter";
private List<Task> mTaskList;
- static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
+ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TaskCardView mTaskCardView;
private Task mTask;
public ViewHolder(View v) {
@@ -58,9 +60,14 @@
@Override
public void onClick(View v) {
try {
- EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
- null, INVALID_STACK_ID));
- ((Activity)(v.getContext())).finish();
+ if (mTaskCardView.isInDismissState()) {
+ mTaskCardView.startDismissTaskAnimation(
+ getRemoveAtListener(getAdapterPosition(), mTaskCardView));
+ } else {
+ EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
+ null, INVALID_STACK_ID));
+ ((Activity) (v.getContext())).finish();
+ }
} catch (Exception e) {
Log.e(TAG, v.getContext()
.getString(R.string.recents_launch_error_message, mTask.title), e);
@@ -97,4 +104,31 @@
public int getItemCount() {
return mTaskList.size();
}
+
+ private Animator.AnimatorListener getRemoveAtListener(final int position,
+ final TaskCardView taskCardView) {
+ return new Animator.AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) { }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeAt(position);
+ EventBus.getDefault().send(new DeleteTaskDataEvent(taskCardView.getTask()));
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) { }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) { }
+ };
+
+ }
+
+ private void removeAt(int position) {
+ mTaskList.remove(position);
+ notifyItemRemoved(position);
+ }
}