Initial Commit of a Horizontal Grid View based recents UI for TV.

Change-Id: I048210e6fc91abafa41300ccb219b7bb9c84e835
This commit is contained in:
Sid Soundararajan
2016-01-26 15:39:27 -08:00
parent d5ee47033f
commit b58c46acec
19 changed files with 1442 additions and 11 deletions

View File

@@ -12,6 +12,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v14-preference \
android-support-v17-leanback \
framework-protos
LOCAL_JAVA_LIBRARIES := telephony-common
@@ -30,10 +31,12 @@ LOCAL_RESOURCE_DIR := \
frameworks/support/v7/preference/res \
frameworks/support/v14/preference/res \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/recyclerview/res
frameworks/support/v7/recyclerview/res \
frameworks/support/v17/leanback/res
LOCAL_AAPT_FLAGS := --auto-add-overlay \
--extra-packages com.android.keyguard:android.support.v7.recyclerview:android.support.v7.preference:android.support.v14.preference:android.support.v7.appcompat
--extra-packages com.android.keyguard:android.support.v7.recyclerview:android.support.v7.preference:android.support.v14.preference:android.support.v7.appcompat \
--extra-packages android.support.v17.leanback
ifneq ($(SYSTEM_UI_INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled

View File

@@ -240,6 +240,20 @@
</intent-filter>
</activity>
<activity android:name=".recents.tv.RecentsTvActivity"
android:label="@string/accessibility_desc_recent_apps"
android:exported="false"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:screenOrientation="behind"
android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
</activity>
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:process=":screenshot"

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.systemui.recents.tv.views.RecentsTvView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recents_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false" >
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:descendantFocusability="beforeDescendants"
android:gravity="center"
android:paddingStart="@dimen/recents_tv_grid_row_padding"
android:paddingEnd="@dimen/recents_tv_grid_row_padding"
android:focusable="true"/>
</com.android.systemui.recents.tv.views.RecentsTvView>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.systemui.recents.tv.views.TaskCardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_gravity="center"
android:layout_centerInParent="true">
<RelativeLayout
android:layout_width="@dimen/recents_tv_card_width"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center">
<ImageView
android:id="@+id/card_view_thumbnail"
android:layout_width="match_parent"
android:layout_height="@dimen/recents_tv_card_height"
android:scaleType="centerCrop"
android:gravity="center"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"/>
<RelativeLayout
android:id="@+id/card_info_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/card_view_thumbnail"
android:background="@color/recents_tv_card_background_color" >
<TextView
android:id="@+id/card_title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="false"
android:includeFontPadding="true"
android:minLines="1"
android:maxLines="2"
android:textColor="@color/recents_tv_card_title_text_color"
android:ellipsize="end" />
<TextView
android:id="@+id/card_content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/card_title_text"
android:includeFontPadding="true"
android:minLines="1"
android:maxLines="2"
android:textColor="@color/recents_tv_card_content_text_color"
android:ellipsize="end" />
<ImageView
android:id="@+id/card_extra_badge"
android:layout_width="@dimen/recents_tv_card_extra_badge_size"
android:layout_height="@dimen/recents_tv_card_extra_badge_size"
android:scaleType="fitCenter"
android:background="@android:color/transparent"
android:contentDescription="@null"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</RelativeLayout>
</com.android.systemui.recents.tv.views.TaskCardView>

View File

@@ -28,9 +28,6 @@
<declare-styleable name="NotificationLinearLayout">
<attr name="insetLeft" format="dimension" />
</declare-styleable>
<declare-styleable name="NotificationRowLayout">
<attr name="rowHeight" format="dimension" />
</declare-styleable>
<declare-styleable name="RecentsPanelView">
<attr name="recentItemLayout" format="reference" />
</declare-styleable>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright 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.
*/
-->
<resources>
<color name="recents_tv_card_background_color">#FF37474F</color>
<color name="recents_tv_card_title_text_color">#FFEEEEEE</color>
<color name="recents_tv_card_content_text_color">#99EEEEEE</color>
<color name="recents_tv_card_source_text_color">#99EEEEEE</color>
</resources>

View File

@@ -606,5 +606,4 @@
<dimen name="docked_divider_handle_width">16dp</dimen>
<dimen name="docked_divider_handle_height">2dp</dimen>
</resources>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright 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.
*/
-->
<resources>
<!-- Dimens for recents card in the recents view on tv -->
<dimen name="recents_tv_card_width">150dip</dimen>
<dimen name="recents_tv_card_height">85dip</dimen>
<dimen name="recents_tv_card_extra_badge_size">16dip</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_grid_max_row_height">200dip</dimen>
<dimen name="recents_tv_gird_card_spacing">8dip</dimen>
<!-- Values for focus animation -->
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
<dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<integer name="item_scale_anim_duration">150</integer>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item format="float" type="raw" name="unselected_scale">1.0</item>
</resources>

View File

@@ -19,10 +19,12 @@ package com.android.systemui.recents;
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;
@@ -87,7 +89,10 @@ 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
* task stacks and update recents accordingly.
@@ -210,6 +215,14 @@ 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;
} else {
mRecentsIntentActivityName = RECENTS_ACTIVITY;
}
}
public void onBootCompleted() {
@@ -906,10 +919,11 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
launchState.launchedViaDragGesture = mDraggingInRecents;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
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 {

View File

@@ -81,6 +81,7 @@ import java.util.Random;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
/**
@@ -266,8 +267,9 @@ public class SystemServicesProxy {
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
if (topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)) {
if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
(topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) ||
topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) {
if (isHomeTopMost != null) {
isHomeTopMost.value = false;
}
@@ -355,6 +357,13 @@ public class SystemServicesProxy {
return stackId == HOME_STACK_ID;
}
/**
* Returns whether the given stack id is the pinned stack id.
*/
public static boolean isPinnedStack(int stackId){
return stackId == PINNED_STACK_ID;
}
/**
* Returns whether the given stack id is the docked stack id.
*/
@@ -968,4 +977,20 @@ public class SystemServicesProxy {
public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
mWm.requestAppKeyboardShortcuts(receiver);
}
public void focusPinnedStack() {
try {
mIam.setFocusedStack(PINNED_STACK_ID);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void focusHomeStack() {
try {
mIam.setFocusedStack(HOME_STACK_ID);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,451 @@
/*
* 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.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager;
import com.android.systemui.R;
import com.android.systemui.recents.*;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.tv.views.RecentsTvView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
import java.util.ArrayList;
/**
* The main TV recents activity started by the RecentsImpl.
*/
public class RecentsTvActivity extends Activity implements OnPreDrawListener {
private final static String TAG = "RecentsTvActivity";
private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
private boolean mFinishedOnStartup;
private RecentsPackageMonitor mPackageMonitor;
private long mLastTabKeyEventTime;
private boolean mIgnoreAltTabRelease;
private RecentsTvView mRecentsView;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
/**
* A common Runnable to finish Recents by launching Home with an animation depending on the
* last activity launch state. Generally we always launch home when we exit Recents rather than
* just finishing the activity since we don't know what is behind Recents in the task stack.
*/
class FinishRecentsRunnable implements Runnable {
Intent mLaunchIntent;
/**
* Creates a finish runnable that starts the specified intent.
*/
public FinishRecentsRunnable(Intent launchIntent) {
mLaunchIntent = launchIntent;
}
@Override
public void run() {
try {
RecentsActivityLaunchState launchState =
Recents.getConfiguration().getLaunchState();
ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsTvActivity.this,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_enter :
R.anim.recents_to_launcher_enter,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_exit :
R.anim.recents_to_launcher_exit);
startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
}
}
}
private void updateRecentsTasks() {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
if (plan == null) {
plan = loader.createLoadPlan(this);
}
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!plan.hasTasks()) {
loader.preloadTasks(plan, -1, launchState.launchedFromHome);
}
TaskStack stack = plan.getTaskStack();
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = stack.getStackTaskCount();
loadOpts.numVisibleTaskThumbnails = stack.getStackTaskCount();
loader.loadTasks(this, plan, loadOpts);
mRecentsView.setTaskStack(stack);
if (mTaskStackViewAdapter == null) {
mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stack.getStackTasks());
mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter);
} else {
mTaskStackViewAdapter.setNewStackTasks(stack.getStackTasks());
}
if (launchState.launchedToTaskId != -1) {
ArrayList<Task> tasks = stack.getStackTasks();
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
if (t.key.id == launchState.launchedToTaskId) {
t.isLaunchTarget = true;
break;
}
}
}
}
boolean dismissRecentsToLaunchTargetTaskOrHome() {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we have a focused Task, launch that Task now
if (mRecentsView.launchPreviousTask()) return true;
// If none of the other cases apply, then just go Home
dismissRecentsToHome(true /* animateTaskViews */);
}
return false;
}
boolean dismissRecentsToFocusedTaskOrHome() {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
// If none of the other cases apply, then just go Home
dismissRecentsToHome(true /* animateTaskViews */);
return true;
}
return false;
}
void dismissRecentsToHome(boolean animateTaskViews) {
DismissRecentsToHomeAnimationStarted dismissEvent =
new DismissRecentsToHomeAnimationStarted(animateTaskViews);
dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
Recents.getSystemServices().sendCloseSystemWindows(
BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
}
});
EventBus.getDefault().send(dismissEvent);
}
boolean dismissRecentsToHomeIfVisible(boolean animated) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// Return to Home
dismissRecentsToHome(animated);
return true;
}
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFinishedOnStartup = false;
// In the case that the activity starts up before the Recents component has initialized
// (usually when debugging/pushing the SysUI apk), just finish this activity.
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp == null) {
mFinishedOnStartup = true;
finish();
return;
}
// Register this activity with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
mPackageMonitor = new RecentsPackageMonitor();
mPackageMonitor.register(this);
// Set the Recents layout
setContentView(R.layout.recents_on_tv);
mRecentsView = (RecentsTvView) findViewById(R.id.recents_view);
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
// Create the home intent runnable
Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
homeIntent.addCategory(Intent.CATEGORY_HOME);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
protected void onStart() {
super.onStart();
// Update the recent tasks
updateRecentsTasks();
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromAppWithThumbnail;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
// Notify that recents is now visible
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@Override
protected void onStop() {
super.onStop();
mIgnoreAltTabRelease = false;
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = false;
launchState.launchedFromSearchHome = false;
launchState.launchedFromAppWithThumbnail = false;
launchState.launchedToTaskId = -1;
launchState.launchedWithAltTab = false;
launchState.launchedHasConfigurationChanged = false;
launchState.launchedViaDragGesture = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
// In the case that the activity finished on startup, just skip the unregistration below
if (mFinishedOnStartup) {
return;
}
// Unregister any broadcast receivers for the task loader
mPackageMonitor.unregister();
EventBus.getDefault().unregister(this);
}
@Override
public void onTrimMemory(int level) {
RecentsTaskLoader loader = Recents.getTaskLoader();
if (loader != null) {
loader.onTrimMemory(level);
}
}
@Override
public void onMultiWindowModeChanged(boolean multiWindowMode) {
super.onMultiWindowModeChanged(multiWindowMode);
if (!multiWindowMode) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
launchOpts.loadIcons = false;
launchOpts.loadThumbnails = false;
launchOpts.onlyLoadForCache = true;
RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
loader.preloadTasks(loadPlan, -1, false);
loader.loadTasks(this, loadPlan, launchOpts);
EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack()));
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP: {
SystemServicesProxy ssp = Recents.getSystemServices();
PipManager.getInstance().showPipMenu();
ssp.focusPinnedStack();
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
SystemServicesProxy ssp = Recents.getSystemServices();
PipManager.getInstance().showPipOverlay(false);
ssp.focusHomeStack();
return true;
}
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_FORWARD_DEL: {
EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
return true;
}
default:
break;
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onUserInteraction() {
EventBus.getDefault().send(new UserInteractionEvent());
}
@Override
public void onBackPressed() {
// Back behaves like the recents button so just trigger a toggle event
EventBus.getDefault().send(new ToggleRecentsEvent());
}
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchState.launchedFromHome) {
dismissRecentsToHome(true /* animateTaskViews */);
} else {
dismissRecentsToLaunchTargetTaskOrHome();
}
}
public final void onBusEvent(HideRecentsEvent event) {
if (event.triggeredFromAltTab) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
if (!mIgnoreAltTabRelease) {
dismissRecentsToFocusedTaskOrHome();
}
} else if (event.triggeredFromHomeKey) {
dismissRecentsToHome(true /* animateTaskViews */);
} else {
// Do nothing
}
}
public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
mRecentsView.invalidate();
}
public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
int launchToTaskId = launchState.launchedToTaskId;
if (launchToTaskId != -1 &&
(event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.cancelWindowTransition(launchState.launchedToTaskId);
ssp.cancelThumbnailTransition(getTaskId());
}
}
public final void onBusEvent(DeleteTaskDataEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.removeTask(event.task.key.id);
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.hasDockedTask()) {
mRecentsView.showEmptyView();
} else {
// Just go straight home (no animation necessary because there are no more task views)
dismissRecentsToHome(false /* animateTaskViews */);
}
}
public final void onBusEvent(LaunchTaskFailedEvent event) {
// Return to Home
dismissRecentsToHome(true /* animateTaskViews */);
}
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
// We post to make sure that this information is delivered after this traversals is
// finished.
mRecentsView.post(new Runnable() {
@Override
public void run() {
Recents.getSystemServices().endProlongedAnimations();
}
});
return true;
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.systemui.R;
public class ViewFocusAnimator implements View.OnFocusChangeListener {
private final float mUnselectedScale;
private final float mSelectedScaleDelta;
private final float mUnselectedZ;
private final float mSelectedZDelta;
private final int mAnimDuration;
private final Interpolator mFocusInterpolator;
protected View mTargetView;
private float mFocusProgress;
ObjectAnimator mFocusAnimation;
public ViewFocusAnimator(View view) {
mTargetView = view;
final Resources res = view.getResources();
mTargetView.setOnFocusChangeListener(this);
TypedValue out = new TypedValue();
res.getValue(R.raw.unselected_scale, out, true);
mUnselectedScale = out.getFloat();
mSelectedScaleDelta = res.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1) -
mUnselectedScale;
mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
mAnimDuration = res.getInteger(R.integer.item_scale_anim_duration);
mFocusInterpolator = new AccelerateDecelerateInterpolator();
mFocusAnimation = ObjectAnimator.ofFloat(this, "focusProgress", 0.0f);
mFocusAnimation.setDuration(mAnimDuration);
mFocusAnimation.setInterpolator(mFocusInterpolator);
setFocusProgress(0.0f);
mFocusAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mTargetView.setHasTransientState(true);
}
@Override
public void onAnimationEnd(Animator animation) {
mTargetView.setHasTransientState(false);
}
});
}
public void setFocusProgress(float level) {
mFocusProgress = level;
float scale = mUnselectedScale + (level * mSelectedScaleDelta);
float z = mUnselectedZ + (level * mSelectedZDelta);
mTargetView.setScaleX(scale);
mTargetView.setScaleY(scale);
mTargetView.setZ(z);
}
public float getFocusProgress() {
return mFocusProgress;
}
public void animateFocus(boolean focused) {
if (mFocusAnimation.isStarted()) {
mFocusAnimation.cancel();
}
float target = focused ? 1.0f : 0.0f;
if (getFocusProgress() != target) {
mFocusAnimation.setFloatValues(getFocusProgress(), target);
mFocusAnimation.start();
}
}
public void setFocusImmediate(boolean focused) {
if (mFocusAnimation.isStarted()) {
mFocusAnimation.cancel();
}
float target = focused ? 1.0f : 0.0f;
setFocusProgress(target);
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v != mTargetView) {
return;
}
changeSize(hasFocus);
}
protected void changeSize(boolean hasFocus) {
ViewGroup.LayoutParams lp = mTargetView.getLayoutParams();
int width = lp.width;
int height = lp.height;
if (width < 0 && height < 0) {
mTargetView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
height = mTargetView.getMeasuredHeight();
}
if (mTargetView.isAttachedToWindow() && mTargetView.hasWindowFocus() &&
mTargetView.getVisibility() == View.VISIBLE) {
animateFocus(hasFocus);
} else {
setFocusImmediate(hasFocus);
}
}
}

View File

@@ -0,0 +1,252 @@
/*
* 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.views;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
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.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.List;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/**
* Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView.
*/
public class RecentsTvView extends FrameLayout {
private static final String TAG = "RecentsTvView";
private static final boolean DEBUG = false;
private TaskStack mStack;
private TaskStackHorizontalGridView mTaskStackHorizontalView;
private View mEmptyView;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
public RecentsTvView(Context context) {
this(context, null);
}
public RecentsTvView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
}
public void setTaskStack(TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
mStack = stack;
if (mTaskStackHorizontalView != null) {
mTaskStackHorizontalView.reset();
mTaskStackHorizontalView.setStack(stack);
} else {
mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
mTaskStackHorizontalView.setStack(stack);
}
if (stack.getStackTaskCount() > 0) {
hideEmptyView();
} else {
showEmptyView();
}
requestLayout();
}
public Task getNextTaskOrTopTask(Task taskToSearch) {
Task returnTask = null;
boolean found = false;
if (mTaskStackHorizontalView != null) {
TaskStack stack = mTaskStackHorizontalView.getStack();
ArrayList<Task> taskList = stack.getStackTasks();
// Iterate the stack views and try and find the focused task
for (int j = taskList.size() - 1; j >= 0; --j) {
Task task = taskList.get(j);
// Return the next task in the line.
if (found)
return task;
// Remember the first possible task as the top task.
if (returnTask == null)
returnTask = task;
if (task == taskToSearch)
found = true;
}
}
return returnTask;
}
public boolean launchFocusedTask() {
if (mTaskStackHorizontalView != null) {
Task task = mTaskStackHorizontalView.getFocusedTask();
if (task != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
return true;
}
}
return false;
}
/** Launches the task that recents was launched from if possible */
public boolean launchPreviousTask() {
if (mTaskStackHorizontalView != null) {
TaskStack stack = mTaskStackHorizontalView.getStack();
Task task = stack.getLaunchTarget();
if (task != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
return true;
}
}
return false;
}
/** Launches a given task. */
public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
if (mTaskStackHorizontalView != null) {
// Iterate the stack views and try and find the given task.
List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
int taskViewCount = taskViews.size();
for (int j = 0; j < taskViewCount; j++) {
TaskCardView tv = taskViews.get(j);
if (tv.getTask() == task) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
return true;
}
}
}
return false;
}
/**
* Hides the task stack and shows the empty view.
*/
public void showEmptyView() {
mEmptyView.setVisibility(View.VISIBLE);
mEmptyView.bringToFront();
}
/**
* Shows the task stack and hides the empty view.
*/
public void hideEmptyView() {
mEmptyView.setVisibility(View.INVISIBLE);
}
/**
* Returns the last known system insets.
*/
public Rect getSystemInsets() {
return mSystemInsets;
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
/**
* This is called with the full size of the window since we are handling our own insets.
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mTaskStackHorizontalView != null && mTaskStackHorizontalView.getVisibility() != GONE) {
mTaskStackHorizontalView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
// Layout the empty view
mEmptyView.layout(left, top, right, bottom);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mSystemInsets.set(insets.getSystemWindowInsets());
requestLayout();
return insets;
}
/**** EventBus Events ****/
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
// If we are going home, cancel the previous task's window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
mStack.createAffiliatedGroupings(getContext());
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
if (!event.visible) {
// Reset the view state
mAwaitingFirstLayout = true;
}
}
public void setTaskStackViewAdapter(TaskStackHorizontalViewAdapter taskStackViewAdapter) {
if(mTaskStackHorizontalView != null) {
mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
import com.android.systemui.recents.model.Task;
public class TaskCardView extends RelativeLayout {
private ImageView mThumbnailView;
private TextView mTitleTextView;
private TextView mContentTextView;
private ImageView mBadgeView;
private Task mTask;
private ViewFocusAnimator mViewFocusAnimator;
public TaskCardView(Context context) {
this(context, null);
}
public TaskCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewFocusAnimator = new ViewFocusAnimator(this);
}
@Override
protected void onFinishInflate() {
mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mContentTextView = (TextView) findViewById(R.id.card_content_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
}
public void init(Task task) {
mTask = task;
mThumbnailView.setImageBitmap(task.thumbnail);
mTitleTextView.setText(task.title);
mContentTextView.setText(task.contentDescription);
mBadgeView.setImageDrawable(task.icon);
}
public Task getTask() {
return mTask;
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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.views;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
import android.content.Context;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.model.TaskStack.TaskStackCallbacks;
import com.android.systemui.recents.views.TaskViewAnimation;
import java.util.ArrayList;
import java.util.List;
/**
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks{
private TaskStack mStack;
private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
public TaskStackHorizontalGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
setItemMargin((int) getResources().getDimension(R.dimen.recents_tv_gird_card_spacing));
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
/**
* Resets this view for reuse.
*/
public void reset() {
// Reset the focused task
resetFocusedTask(getFocusedTask());
requestLayout();
}
/**
* @param task - Task to reset
*/
private void resetFocusedTask(Task task) {
if (task != null) {
TaskCardView tv = getChildViewForTask(task);
if (tv != null) {
tv.requestFocus();
}
}
mFocusedTask = null;
}
/**
* Sets the task stack.
* @param stack
*/
public void setStack(TaskStack stack) {
//Set new stack
mStack = stack;
if (mStack != null) {
mStack.setCallbacks(this);
}
//Layout with new stack
requestLayout();
}
/**
* @return Returns the task stack.
*/
public TaskStack getStack() {
return mStack;
}
/**
* @return - The focused task.
*/
public Task getFocusedTask() {
return mFocusedTask;
}
/**
* @param task
* @return Child view for given task
*/
public TaskCardView getChildViewForTask(Task task) {
List<TaskCardView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
TaskCardView tv = taskViews.get(i);
if (tv.getTask() == task) {
return tv;
}
}
return null;
}
public List<TaskCardView> getTaskViews() {
return mTaskViews;
}
@Override
public void onStackTaskAdded(TaskStack stack, Task newTask){
getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
}
@Override
public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Task newFrontMostTask, TaskViewAnimation animation) {
getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask));
if (mFocusedTask == removedTask) {
resetFocusedTask(removedTask);
}
// If there are no remaining tasks, then just close recents
if (mStack.getStackTaskCount() == 0) {
boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
if (shouldFinishActivity) {
EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
}
}
}
@Override
public void onHistoryTaskRemoved(TaskStack stack, Task removedTask, TaskViewAnimation animation) {
//No history task on tv
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.views;
import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.recents.model.Task;
import android.support.v7.widget.RecyclerView;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import java.util.ArrayList;
import java.util.List;
public class TaskStackHorizontalViewAdapter extends
RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> {
private static final String TAG = "TaskStackHorizontalViewAdapter";
private List<Task> mTaskList;
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TaskCardView mTaskCardView;
private Task mTask;
public ViewHolder(View v) {
super(v);
if(v instanceof TaskCardView) {
mTaskCardView = (TaskCardView) v;
}
}
public void init(Task task) {
mTaskCardView.init(task);
mTask = task;
mTaskCardView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
try {
ActivityManagerNative.getDefault().startActivityFromRecents(mTask.key.id, null);
((Activity)(v.getContext())).finish();
} catch (Exception e) {
Log.e(TAG, v.getContext()
.getString(R.string.recents_launch_error_message, mTask.title), e);
}
}
}
public TaskStackHorizontalViewAdapter(List tasks) {
mTaskList = new ArrayList<>(tasks);
}
public void setNewStackTasks(List tasks) {
mTaskList.clear();
mTaskList.addAll(tasks);
notifyDataSetChanged();
}
@Override
public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recents_task_card_view, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.init(mTaskList.get(position));
}
@Override
public int getItemCount() {
return mTaskList.size();
}
}

View File

@@ -17,12 +17,15 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
LOCAL_AAPT_FLAGS := --auto-add-overlay \
--extra-packages com.android.systemui:com.android.keyguard:android.support.v14.preference:android.support.v7.preference:android.support.v7.appcompat:android.support.v7.recyclerview
--extra-packages com.android.systemui:com.android.keyguard:android.support.v14.preference:android.support.v7.preference:android.support.v7.appcompat:android.support.v7.recyclerview \
--extra-packages android.support.v17.leanback
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-Iaidl-files-under, src) \
@@ -35,6 +38,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
frameworks/support/v14/preference/res \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/recyclerview/res \
frameworks/support/v17/leanback/res \
frameworks/base/packages/SystemUI/res \
frameworks/base/packages/Keyguard/res
@@ -48,7 +52,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v14-preference
android-support-v14-preference \
android-support-v17-leanback
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken