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 0000000000000..73f5116bd71f6 Binary files /dev/null and b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png 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 0000000000000..787e2593789b2 Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png 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 0000000000000..6ebbc831605f2 Binary files /dev/null and b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png differ diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml index 567e009d07eec..a00c7449cde24 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" /> - + + + + \ 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 af99aaecf62a8..4126d3cdad684 100644 --- a/packages/SystemUI/res/values/colors_tv.xml +++ b/packages/SystemUI/res/values/colors_tv.xml @@ -19,4 +19,5 @@ #FF37474F #CCEEEEEE + #7FEEEEEE \ 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 6b153d1117eaa..953dd650b2aaa 100644 --- a/packages/SystemUI/res/values/dimens_tv.xml +++ b/packages/SystemUI/res/values/dimens_tv.xml @@ -18,8 +18,8 @@ --> - 268dip - 151dip + 240dip + 135dip 20dip 114dip 64dip @@ -29,10 +29,10 @@ 12dip - 56dip - 57dip + 215dip 268dip - 20dip + 8dip + 44dip 6dp @@ -43,4 +43,13 @@ 12sp + + + 48dip + 356dip + 24dip + 38dip + 1dip + 12sp + diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml index bfd8f8beb958d..c60c24556ca69 100644 --- a/packages/SystemUI/res/values/integers_tv.xml +++ b/packages/SystemUI/res/values/integers_tv.xml @@ -15,4 +15,6 @@ --> 150 + 200 + 400 \ 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 0e1fe8fa3384a..da6c279fb0c4c 100644 --- a/packages/SystemUI/res/values/strings_tv.xml +++ b/packages/SystemUI/res/values/strings_tv.xml @@ -35,7 +35,11 @@ Press and hold the HOME button to control PIP Got it + + Dismiss sans-serif + + sans-serif-light diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml index 6a72e542482b0..bd72c5115566f 100644 --- a/packages/SystemUI/res/values/values_tv.xml +++ b/packages/SystemUI/res/values/values_tv.xml @@ -15,5 +15,5 @@ limitations under the License. --> 1.0 - 1.1 + 1.259 diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 2b6ed445a8103..da07aecd2c0b7 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.component.ScreenPinningRequestEvent; 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 @@ public class Recents extends SystemUI 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 9be24def0da47..880fe10e2b796 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 static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 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.RecentsTaskLoader; 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 class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } } - 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } }); - Bitmap mThumbnailTransitionBitmapCache; + protected Bitmap mThumbnailTransitionBitmapCache; Task mThumbnailTransitionBitmapCacheKey; public RecentsImpl(Context context) { @@ -227,16 +219,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener /** * 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener /** * 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } } - /** - * 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 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener /** * 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. @@ -971,90 +932,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener mLastToggleTime = SystemClock.elapsedRealtime(); } - /** - * 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. */ @@ -1078,7 +955,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener 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 532e79661c6ae..f7807e53c3f36 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.Rect; 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.Prefs; 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 @@ public class SystemServicesProxy { // 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 0000000000000..9fd5d55ed722f --- /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 0000000000000..8996d0b2e8c23 --- /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 365b29d676762..888561c2172b9 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 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { 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 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { 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 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { 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 5775b60ab3082..3343aec8891b7 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.graphics.Rect; 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 @@ public class TaskCardView extends LinearLayout { 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 class TaskCardView extends LinearLayout { public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mViewFocusAnimator = new ViewFocusAnimator(this); + mDismissState = false; } @Override @@ -58,6 +64,7 @@ public class TaskCardView extends LinearLayout { 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 @@ public class TaskCardView extends LinearLayout { 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 @@ public class TaskCardView extends LinearLayout { 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 4458639acfad6..29a72da68a774 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 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T private ArrayList mTaskViews = new ArrayList<>(); private Task mFocusedTask; - public TaskStackHorizontalGridView(Context context) { this(context, null); } @@ -53,7 +52,7 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T @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(); } @@ -108,6 +107,13 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T return mFocusedTask; } + /** + * @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 fba424ee2ee33..378871934f704 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 android.view.ViewGroup; 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 @@ public class TaskStackHorizontalViewAdapter extends private static final String TAG = "TaskStackViewAdapter"; private List 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 @@ public class TaskStackHorizontalViewAdapter extends @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 class TaskStackHorizontalViewAdapter extends 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); + } }