Initial Commit of a Horizontal Grid View based recents UI for TV.
Change-Id: I048210e6fc91abafa41300ccb219b7bb9c84e835
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
37
packages/SystemUI/res/layout/recents_on_tv.xml
Normal file
37
packages/SystemUI/res/layout/recents_on_tv.xml
Normal 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>
|
||||
|
||||
77
packages/SystemUI/res/layout/recents_task_card_view.xml
Normal file
77
packages/SystemUI/res/layout/recents_task_card_view.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
24
packages/SystemUI/res/values/colors_tv.xml
Normal file
24
packages/SystemUI/res/values/colors_tv.xml
Normal 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>
|
||||
@@ -606,5 +606,4 @@
|
||||
|
||||
<dimen name="docked_divider_handle_width">16dp</dimen>
|
||||
<dimen name="docked_divider_handle_height">2dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
34
packages/SystemUI/res/values/dimens_tv.xml
Normal file
34
packages/SystemUI/res/values/dimens_tv.xml
Normal 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>
|
||||
18
packages/SystemUI/res/values/integers_tv.xml
Normal file
18
packages/SystemUI/res/values/integers_tv.xml
Normal 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>
|
||||
18
packages/SystemUI/res/values/values_tv.xml
Normal file
18
packages/SystemUI/res/values/values_tv.xml
Normal 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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user