Adding history view.

- Initial changes to show a history view within Overview (behind tuner
  flag)
- Restoring the task view dim in the stack

Change-Id: I0503d11768736c86f3145942404391dfacd0ddd6
This commit is contained in:
Winson
2015-11-20 16:00:45 -08:00
parent 7afe8c9002
commit c29ff0025b
23 changed files with 853 additions and 41 deletions

View File

@@ -6,7 +6,9 @@ LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
LOCAL_STATIC_JAVA_LIBRARIES := \
Keyguard \
android-support-v7-recyclerview
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI

View File

@@ -39,6 +39,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- History View -->
<ViewStub android:id="@+id/history_view_stub"
android:layout="@layout/recents_history"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Nav Bar Scrim View -->
<ImageView
android:id="@+id/nav_bar_scrim"

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.systemui.recents.history.RecentsHistoryView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99000000"
android:orientation="vertical">
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="14dp"
android:gravity="start"
android:text="@string/recents_history_label"
android:textSize="24sp"
android:textColor="#FFFFFF"
android:fontFamily="sans-serif-medium" />
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.systemui.recents.history.RecentsHistoryView>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/recents_history_button_height"
android:gravity="start|center_vertical"
android:text="@string/recents_show_history_button_label"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:textAllCaps="true"
android:fontFamily="sans-serif-medium"
android:visibility="invisible" />

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.Material"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:gravity="start"
android:textSize="14sp"
android:textColor="#009688"
android:textAllCaps="true"
android:fontFamily="sans-serif-medium"
android:background="?android:selectableItemBackground"
android:alpha="1" />

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.Material"
android:layout_width="match_parent"
android:layout_height="48dp"
android:paddingLeft="32dp"
android:gravity="start|center_vertical"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:fontFamily="sans-serif-medium"
android:background="?android:selectableItemBackground"
android:alpha="1" />

View File

@@ -177,6 +177,9 @@
<!-- The animation duration for scrolling the stack to a particular item. -->
<integer name="recents_animate_task_stack_scroll_duration">225</integer>
<!-- The animation duration for entering and exiting the history. -->
<integer name="recents_history_transition_duration">250</integer>
<!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
<integer name="recents_max_task_stack_view_dim">96</integer>

View File

@@ -698,6 +698,10 @@
<string name="recents_search_bar_label">search</string>
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_show_history_button_label">More</string>
<!-- Recents: The history of recents. [CHAR LIMIT=NONE] -->
<string name="recents_history_label">History</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1157,21 +1161,26 @@
<!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
<string name="qs_paging" translatable="false">Use the new Quick Settings</string>
<!-- Toggles paging recents via the recents button -->
<!-- Toggles paging recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_page_on_toggle">Enable paging</string>
<!-- Description for the toggle for fast-toggling recents via the recents button -->
<!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_page_on_toggle_desc">Enable paging via the Overview button</string>
<!-- Toggles fast-toggling recents via the recents button -->
<!-- Toggles fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_fast_toggle_via_button">Enable fast toggle</string>
<!-- Description for the toggle for fast-toggling recents via the recents button -->
<!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
<!-- Toggles fullscreen screenshots -->
<!-- Toggles fullscreen screenshots. DO NOT TRANSLATE -->
<string name="overview_fullscreen_thumbnails">Enable fullscreen screenshots</string>
<!-- Description for the toggle for fullscreen screenshots -->
<!-- Description for the toggle for fullscreen screenshots. DO NOT TRANSLATE -->
<string name="overview_fullscreen_thumbnails_desc">Enable fullscreen screenshots in Overview</string>
<!-- Toggle to show the history view in Overview. DO NOT TRANSLATE -->
<string name="overview_show_history">Show History</string>
<!-- Description for the toggle to show the history view in Overview. DO NOT TRANSLATE -->
<string name="overview_show_history_desc">Enables the history view to see more recent tasks</string>
<!-- Category in the System UI Tuner settings, where new/experimental
settings are -->
<string name="experimental">Experimental</string>

View File

@@ -102,6 +102,11 @@
android:title="@string/overview_fullscreen_thumbnails"
android:summary="@string/overview_fullscreen_thumbnails_desc" />
<com.android.systemui.tuner.TunerSwitch
android:key="overview_show_history"
android:title="@string/overview_show_history"
android:summary="@string/overview_show_history_desc" />
</PreferenceScreen>
<SwitchPreference

View File

@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -46,10 +47,12 @@ import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnim
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -65,6 +68,7 @@ import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.history.RecentsHistoryView;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -99,6 +103,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
private SystemBarScrimViews mScrimViews;
private ViewStub mEmptyViewStub;
private View mEmptyView;
private ViewStub mHistoryViewStub;
private RecentsHistoryView mHistoryView;
// Resize task debug
private RecentsResizeTaskDialog mResizeTaskDebugDialog;
@@ -259,6 +265,18 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
/**
* Dismisses the history view back into the stack view.
*/
boolean dismissHistory() {
// Try and hide the history view first
if (mHistoryView != null && mHistoryView.isVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
return true;
}
return false;
}
/**
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
@@ -349,6 +367,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
mHistoryViewStub = (ViewStub) findViewById(R.id.history_view_stub);
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -432,6 +451,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// Reset some states
mIgnoreAltTabRelease = false;
if (mHistoryView != null) {
EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -550,8 +572,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
@Override
public void onBackPressed() {
// Dismiss Recents to the focused Task or Home
dismissRecentsToFocusedTaskOrHome();
if (!dismissHistory()) {
dismissRecentsToFocusedTaskOrHome();
}
}
/**** RecentsResizeTaskDialog ****/
@@ -566,7 +589,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
dismissRecentsToFocusedTaskOrHome();
if (!dismissHistory()) {
dismissRecentsToFocusedTaskOrHome();
}
}
public final void onBusEvent(IterateRecentsEvent event) {
@@ -596,7 +621,21 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
dismissRecentsToHome(true /* animated */);
if (mHistoryView != null && mHistoryView.isVisible()) {
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
t.increment();
t.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
dismissRecentsToHome(true /* animated */);
}
});
EventBus.getDefault().send(new HideHistoryEvent(true, t));
t.decrement();
} else {
dismissRecentsToHome(true /* animated */);
}
} else {
// Do nothing
}
@@ -726,6 +765,23 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
mIgnoreAltTabRelease = true;
}
public final void onBusEvent(ShowHistoryEvent event) {
if (mHistoryView == null) {
mHistoryView = (RecentsHistoryView) mHistoryViewStub.inflate();
// Since this history view is inflated by a view stub after the insets have already
// been applied, we have to set them ourselves initial from the insets that were last
// provided.
mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
}
mHistoryView.show(mRecentsView.getTaskStack());
}
public final void onBusEvent(HideHistoryEvent event) {
if (mHistoryView != null) {
mHistoryView.hide(event.animate, event.postAnimationTrigger);
}
}
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = Recents.getSystemServices();

View File

@@ -29,6 +29,7 @@ public class RecentsDebugFlags implements TunerService.Tunable {
private static final String KEY_FAST_TOGGLE = "overview_fast_toggle";
private static final String KEY_PAGE_ON_TOGGLE = "overview_page_on_toggle";
private static final String KEY_FULLSCREEN_THUMBNAILS = "overview_fullscreen_thumbnails";
private static final String KEY_SHOW_HISTORY = "overview_show_history";
public static class Static {
// Enables debug drawing for the transition thumbnail
@@ -52,6 +53,7 @@ public class RecentsDebugFlags implements TunerService.Tunable {
private boolean mFastToggleRecents;
private boolean mPageOnToggle;
private boolean mUseFullscreenThumbnails;
private boolean mShowHistory;
/**
* We read the prefs once when we start the activity, then update them as the tuner changes
@@ -61,7 +63,7 @@ public class RecentsDebugFlags implements TunerService.Tunable {
// Register all our flags, this will also call onTuningChanged() for each key, which will
// initialize the current state of each flag
TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_PAGE_ON_TOGGLE,
KEY_FULLSCREEN_THUMBNAILS);
KEY_FULLSCREEN_THUMBNAILS, KEY_SHOW_HISTORY);
}
/**
@@ -85,6 +87,13 @@ public class RecentsDebugFlags implements TunerService.Tunable {
return mUseFullscreenThumbnails;
}
/**
* @return whether we should show the history
*/
public boolean isHistoryEnabled() {
return mShowHistory;
}
@Override
public void onTuningChanged(String key, String newValue) {
switch (key) {
@@ -100,6 +109,10 @@ public class RecentsDebugFlags implements TunerService.Tunable {
mUseFullscreenThumbnails = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
break;
case KEY_SHOW_HISTORY:
mShowHistory = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
break;
}
EventBus.getDefault().send(new DebugFlagsChangedEvent());
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view button should be hidden.
*/
public class HideHistoryButtonEvent extends EventBus.Event {
// Simple event
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/**
* This is sent when the history view will be closed.
*/
public class HideHistoryEvent extends EventBus.Event {
public final boolean animate;
public final ReferenceCountedTrigger postAnimationTrigger;
public HideHistoryEvent(boolean animate) {
this(animate, null);
}
public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postAnimationTrigger) {
this.animate = animate;
this.postAnimationTrigger = postAnimationTrigger;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view button should be shown.
*/
public class ShowHistoryButtonEvent extends EventBus.Event {
// Simple event
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view button is clicked.
*/
public class ShowHistoryEvent extends EventBus.Event {
// Simple event
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.history;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
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.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* An adapter for the list of recent tasks in the history view.
*/
public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAdapter.ViewHolder> {
private static final String TAG = "RecentsHistoryView";
private static final boolean DEBUG = false;
static final int DATE_ROW_VIEW_TYPE = 0;
static final int TASK_ROW_VIEW_TYPE = 1;
/**
* View holder implementation.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public View mContent;
public ViewHolder(View v) {
super(v);
mContent = v;
}
}
/**
* A single row of content.
*/
private interface Row {
int getViewType();
}
/**
* A date row.
*/
private static class DateRow implements Row {
public final String date;
public DateRow(String date) {
this.date = date;
}
@Override
public int getViewType() {
return RecentsHistoryAdapter.DATE_ROW_VIEW_TYPE;
}
}
/**
* A task row.
*/
private static class TaskRow implements Row, View.OnClickListener {
public final String description;
private final int mTaskId;
public TaskRow(Task task) {
mTaskId = task.key.id;
description = task.activityLabel;
}
@Override
public void onClick(View v) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(v.getContext(), mTaskId, description,
ActivityOptions.makeBasic());
}
@Override
public int getViewType() {
return RecentsHistoryAdapter.TASK_ROW_VIEW_TYPE;
}
}
private LayoutInflater mLayoutInflater;
private final List<Row> mRows = new ArrayList<>();
public RecentsHistoryAdapter(Context context, TaskStack stack) {
mLayoutInflater = LayoutInflater.from(context);
updateTasks(context, stack.getTasks());
}
/**
* Updates this adapter with the given tasks.
*/
public void updateTasks(Context context, List<Task> tasks) {
final Locale l = context.getResources().getConfiguration().locale;
final String dateFormatStr = DateFormat.getBestDateTimePattern(l, "EEEEMMMMd");
final List<Task> tasksMostRecent = new ArrayList<>(tasks);
Collections.reverse(tasksMostRecent);
int prevDayKey = -1;
mRows.clear();
for (Task task : tasksMostRecent) {
if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
continue;
}
Calendar cal = Calendar.getInstance(l);
cal.setTimeInMillis(task.key.lastActiveTime);
int dayKey = Objects.hash(cal.get(Calendar.YEAR), cal.get(Calendar.DAY_OF_YEAR));
if (dayKey != prevDayKey) {
prevDayKey = dayKey;
mRows.add(new DateRow(DateFormat.format(dateFormatStr, cal).toString()));
}
mRows.add(new TaskRow(task));
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case DATE_ROW_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.recents_history_date, parent,
false));
case TASK_ROW_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.recents_history_task, parent,
false));
default:
return new ViewHolder(null);
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Row row = mRows.get(position);
int viewType = mRows.get(position).getViewType();
switch (viewType) {
case DATE_ROW_VIEW_TYPE: {
TextView tv = (TextView) holder.mContent;
tv.setText(((DateRow) row).date);
break;
}
case TASK_ROW_VIEW_TYPE: {
TextView tv = (TextView) holder.mContent;
TaskRow taskRow = (TaskRow) row;
tv.setText(taskRow.description);
tv.setOnClickListener(taskRow);
break;
}
}
}
@Override
public int getItemCount() {
return mRows.size();
}
@Override
public int getItemViewType(int position) {
return mRows.get(position).getViewType();
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.history;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.TaskStack;
/**
* A list of the recent tasks that are not in the stack.
*/
public class RecentsHistoryView extends LinearLayout {
private static final String TAG = "RecentsHistoryView";
private static final boolean DEBUG = false;
private RecyclerView mRecyclerView;
private boolean mIsVisible;
private Interpolator mFastOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
private int mHistoryTransitionDuration;
public RecentsHistoryView(Context context) {
super(context);
}
public RecentsHistoryView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RecentsHistoryView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public RecentsHistoryView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = context.getResources();
mHistoryTransitionDuration = res.getInteger(R.integer.recents_history_transition_duration);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
}
/**
* Updates this history view with the recent tasks, and then shows it.
*/
public void show(TaskStack stack) {
setVisibility(View.VISIBLE);
setAlpha(0f);
animate()
.alpha(1f)
.setDuration(mHistoryTransitionDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.withLayer()
.start();
mRecyclerView.setAdapter(new RecentsHistoryAdapter(getContext(), stack));
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mIsVisible = true;
}
/**
* Hides this history view.
*/
public void hide(boolean animate, final ReferenceCountedTrigger postAnimationTrigger) {
if (animate) {
animate()
.alpha(0f)
.setDuration(mHistoryTransitionDuration)
.setInterpolator(mFastOutLinearInInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
setVisibility(View.INVISIBLE);
if (postAnimationTrigger != null) {
postAnimationTrigger.decrement();
}
}
})
.withLayer()
.start();
if (postAnimationTrigger != null) {
postAnimationTrigger.increment();
}
} else {
setAlpha(0f);
setVisibility(View.INVISIBLE);
}
mIsVisible = false;
}
/**
* Updates the system insets of this history view to the provided values.
*/
public void setSystemInsets(Rect systemInsets) {
setPadding(systemInsets.left, systemInsets.top, systemInsets.right, systemInsets.bottom);
}
/**
* Returns whether this view is visible.
*/
public boolean isVisible() {
return mIsVisible;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mRecyclerView = (RecyclerView) findViewById(R.id.list);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
setSystemInsets(insets.getSystemWindowInsets());
return insets;
}
}

View File

@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -25,20 +26,26 @@ import android.util.ArraySet;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
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.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
@@ -63,11 +70,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
LayoutInflater mInflater;
Handler mHandler;
TaskStack mStack;
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
View mHistoryButton;
boolean mAwaitingFirstLayout = true;
boolean mLastTaskLaunchedWasFreeform;
@@ -81,7 +89,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
TaskStack.DockState.BOTTOM,
};
Interpolator mFastOutSlowInInterpolator;
private Interpolator mFastOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
private int mHistoryTransitionDuration;
Rect mSystemInsets = new Rect();
@@ -99,18 +109,31 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = context.getResources();
setWillNotDraw(false);
mInflater = LayoutInflater.from(context);
mHandler = new Handler();
mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
mHistoryTransitionDuration = res.getInteger(R.integer.recents_history_transition_duration);
mTouchHandler = new RecentsViewTouchHandler(this);
LayoutInflater inflater = LayoutInflater.from(context);
mHistoryButton = inflater.inflate(R.layout.recents_history_button, this, false);
mHistoryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().send(new ShowHistoryEvent());
}
});
}
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
mStack = stack;
if (config.getLaunchState().launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
@@ -129,6 +152,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mTaskStackView.setCallbacks(this);
addView(mTaskStackView);
}
if (indexOfChild(mHistoryButton) == -1) {
addView(mHistoryButton);
} else {
mHistoryButton.bringToFront();
}
// Trigger a new layout
requestLayout();
@@ -141,6 +169,13 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return mLastTaskLaunchedWasFreeform;
}
/**
* Returns the currently set task stack.
*/
public TaskStack getTaskStack() {
return mStack;
}
/** Gets the next task in the stack - or if the last - the top task */
public Task getNextTaskOrTopTask(Task taskToSearch) {
Task returnTask = null;
@@ -219,10 +254,15 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
ctx.postAnimationTrigger.decrement();
// Hide the history button
int taskViewExitToHomeDuration = getResources().getInteger(
R.integer.recents_task_exit_to_home_duration);
hideHistoryButton(taskViewExitToHomeDuration);
// If we are going home, cancel the previous task's window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
// Notify of the exit animation
// Notify sof the exit animation
EventBus.getDefault().send(new DismissRecentsToHomeAnimationStarted());
}
@@ -253,6 +293,13 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
/**
* Returns the last known system insets.
*/
public Rect getSystemInsets() {
return mSystemInsets;
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -301,6 +348,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
MeasureSpec.makeMeasureSpec(taskRect.height(), MeasureSpec.AT_MOST));
}
Rect stackRect = mTaskStackView.mLayoutAlgorithm.mCurrentStackRect;
measureChild(mHistoryButton,
MeasureSpec.makeMeasureSpec(stackRect.width(), MeasureSpec.EXACTLY),
heightMeasureSpec);
setMeasuredDimension(width, height);
}
@@ -330,6 +381,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
top + mDragView.getMeasuredHeight());
}
Rect stackRect = mTaskStackView.mLayoutAlgorithm.mCurrentStackRect;
mHistoryButton.layout(stackRect.left, stackRect.top,
stackRect.left + mHistoryButton.getMeasuredWidth(),
stackRect.top + mHistoryButton.getMeasuredHeight());
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
@@ -346,7 +402,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mSystemInsets.set(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets();
return insets;
}
@Override
@@ -492,6 +548,67 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
animate().translationY(0f);
}
public final void onBusEvent(ShowHistoryEvent event) {
// Hide the history button when the history view is shown
hideHistoryButton(mHistoryTransitionDuration);
}
public final void onBusEvent(HideHistoryEvent event) {
// Show the history button when the history view is hidden
showHistoryButton(mHistoryTransitionDuration);
}
public final void onBusEvent(ShowHistoryButtonEvent event) {
showHistoryButton(150);
}
public final void onBusEvent(HideHistoryButtonEvent event) {
hideHistoryButton(100);
}
public final void onBusEvent(DebugFlagsChangedEvent event) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
if (!debugFlags.isHistoryEnabled()) {
hideHistoryButton(100);
}
}
/**
* Shows the history button.
*/
private void showHistoryButton(int duration) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
if (!debugFlags.isHistoryEnabled()) {
return;
}
mHistoryButton.setVisibility(View.VISIBLE);
mHistoryButton.animate()
.alpha(1f)
.setDuration(duration)
.setInterpolator(mFastOutSlowInInterpolator)
.withLayer()
.start();
}
/**
* Hides the history button.
*/
private void hideHistoryButton(int duration) {
mHistoryButton.animate()
.alpha(0f)
.setDuration(duration)
.setInterpolator(mFastOutLinearInInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
mHistoryButton.setVisibility(View.INVISIBLE);
}
})
.withLayer()
.start();
}
/**
* Updates the dock region to match the specified dock state.
*/

View File

@@ -573,7 +573,7 @@ public class TaskStackLayoutAlgorithm {
(mCurrentStackRect.height() - mTaskRect.height()) / 2;
y = centerYOffset + getYForDeltaP(p, 0);
z = mMaxTranslationZ;
relP = p;
relP = 1f;
} else {
// Otherwise, update the task to the stack layout

View File

@@ -42,8 +42,12 @@ import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -82,6 +86,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
private final static String TAG = "TaskStackView";
private final static boolean DEBUG = false;
// The thresholds at which to show/hide the history button.
private static final float SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
@@ -1017,6 +1025,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
false /* requestViewFocus */);
}
// Update the history button visibility
if (mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
}
// Start dozing
mUIDozeTrigger.startDozing();
}
@@ -1333,10 +1346,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
public void onScrollChanged(float p) {
public void onScrollChanged(float prevScroll, float curScroll) {
mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
postInvalidateOnAnimation();
if (prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
} else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new HideHistoryButtonEvent());
}
}
/**** EventBus Events ****/
@@ -1496,15 +1517,30 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
public final void onBusEvent(ShowHistoryEvent event) {
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
tv.animate().alpha(0f).setDuration(200).start();
}
}
public final void onBusEvent(HideHistoryEvent event) {
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
tv.animate().alpha(1f).setDuration(200).start();
}
}
/**
* Removes the task from the stack, and updates the focus to the next task in the stack if the
* removed TaskView was focused.
*/
private void removeTaskViewFromStack(TaskView tv) {
SystemServicesProxy ssp = Recents.getSystemServices();
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
boolean taskWasFocused = tv.isFocusedTask();
// Reset the previously focused task before it is removed from the stack
resetFocusedTask();

View File

@@ -35,7 +35,7 @@ public class TaskStackViewScroller {
private static final boolean DEBUG = false;
public interface TaskStackViewScrollerCallbacks {
void onScrollChanged(float p);
void onScrollChanged(float prevScroll, float curScroll);
}
Context mContext;
@@ -78,9 +78,10 @@ public class TaskStackViewScroller {
/** Sets the current stack scroll */
public void setStackScroll(float s) {
float prevStackScroll = mStackScrollP;
mStackScrollP = s;
if (mCb != null) {
mCb.onScrollChanged(mStackScrollP);
mCb.onScrollChanged(prevStackScroll, mStackScrollP);
}
}

View File

@@ -71,7 +71,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
int mDimAlpha;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1f);
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
@@ -265,15 +265,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
toTransform.translationZ = 0;
}
/**
* When we are un/filtering, this method will setup the transform that we are animating from,
* in order to show the task.
*/
void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
// Fade the view in
fromTransform.alpha = 0f;
}
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean hideTask,
@@ -617,12 +608,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** Compute the dim as a function of the scale of this view. */
int getDimFromTaskProgress() {
// TODO: Temporarily disable the dim on the stack
/*
float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
float x = mTaskProgress < 0 ? 1f : mDimInterpolator.getInterpolation(1f - mTaskProgress);
float dim = mMaxDimScale * x;
return (int) (dim * 255);
*/
return 0;
}
/** Update the dim as a function of the scale of this view. */

View File

@@ -37,7 +37,10 @@ LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
LOCAL_PACKAGE_NAME := SystemUITests
LOCAL_STATIC_JAVA_LIBRARIES := mockito-target Keyguard
LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-target \
Keyguard \
android-support-v7-recyclerview
# 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