summaryrefslogtreecommitdiffstats
path: root/quickstep/recents_ui_overrides
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2019-06-13 19:09:00 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-06-13 19:09:00 +0000
commit552b3fe811857c475327ecc152f6923c822670f1 (patch)
treef618fb97242ac35619d84249c29bc30428f43bbd /quickstep/recents_ui_overrides
parent6e25a9d8319a7df686e12996c01f2f6b6416c21e (diff)
parent5b636ebfd4a8a2ef53a11d0190c59dd0fb9aad75 (diff)
downloadandroid_packages_apps_Trebuchet-552b3fe811857c475327ecc152f6923c822670f1.tar.gz
android_packages_apps_Trebuchet-552b3fe811857c475327ecc152f6923c822670f1.tar.bz2
android_packages_apps_Trebuchet-552b3fe811857c475327ecc152f6923c822670f1.zip
Merge "Optimizing some layouts in taskview" into ub-launcher3-qt-dev
Diffstat (limited to 'quickstep/recents_ui_overrides')
-rw-r--r--quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml7
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java105
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java3
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java193
4 files changed, 220 insertions, 88 deletions
diff --git a/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml b/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml
deleted file mode 100644
index be3f17acd..000000000
--- a/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<com.android.quickstep.hints.ProactiveHintsContainer
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="center_horizontal">
-</com.android.quickstep.hints.ProactiveHintsContainer> \ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 5aab944a3..7fac81385 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -18,9 +18,11 @@ package com.android.quickstep.views;
import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
+import static com.android.launcher3.Utilities.prefixTextWithIcon;
+
+import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.AppUsageLimit;
@@ -28,16 +30,16 @@ import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
+import android.os.Build;
import android.os.UserHandle;
-import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.StringRes;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -46,45 +48,72 @@ import com.android.systemui.shared.recents.model.Task;
import java.time.Duration;
import java.util.Locale;
-public final class DigitalWellBeingToast extends LinearLayout {
+@TargetApi(Build.VERSION_CODES.Q)
+public final class DigitalWellBeingToast {
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
+
+ private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
+
+ private final BaseDraggingActivity mActivity;
+ private final TaskView mTaskView;
private final LauncherApps mLauncherApps;
- public interface InitializeCallback {
- void call(String contentDescription);
+ private Task mTask;
+ private boolean mHasLimit;
+ private long mAppRemainingTimeMs;
+
+ public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
+ mActivity = activity;
+ mTaskView = taskView;
+ mLauncherApps = activity.getSystemService(LauncherApps.class);
}
- private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
+ private void setTaskFooter(View view) {
+ View oldFooter = mTaskView.setFooter(TaskView.INDEX_DIGITAL_WELLBEING_TOAST, view);
+ if (oldFooter != null) {
+ oldFooter.setOnClickListener(null);
+ mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, oldFooter);
+ }
+ }
- private Task mTask;
- private TextView mText;
-
- public DigitalWellBeingToast(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayoutDirection(Utilities.isRtl(getResources()) ?
- View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
- setOnClickListener((view) -> openAppUsageSettings());
- mLauncherApps = context.getSystemService(LauncherApps.class);
+ private void setNoLimit() {
+ mHasLimit = false;
+ mTaskView.setContentDescription(mTask.titleDescription);
+ setTaskFooter(null);
+ mAppRemainingTimeMs = 0;
}
- public TextView getTextView() {
- return mText;
+ private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
+ mAppRemainingTimeMs = appRemainingTimeMs;
+ mHasLimit = true;
+ TextView toast = mActivity.getViewCache().getView(R.layout.digital_wellbeing_toast,
+ mActivity, mTaskView);
+ toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText()));
+ toast.setOnClickListener(this::openAppUsageSettings);
+ setTaskFooter(toast);
+
+ mTaskView.setContentDescription(
+ getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
+ RecentsView rv = mTaskView.getRecentsView();
+ if (rv != null) {
+ rv.onDigitalWellbeingToastShown();
+ }
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ public String getText() {
+ return getText(mAppRemainingTimeMs);
+ }
- mText = findViewById(R.id.digital_well_being_remaining_time);
+ public boolean hasLimit() {
+ return mHasLimit;
}
- public void initialize(Task task, InitializeCallback callback) {
+ public void initialize(Task task) {
mTask = task;
if (task.key.userId != UserHandle.myUserId()) {
- setVisibility(GONE);
- callback.call(task.titleDescription);
+ setNoLimit();
return;
}
@@ -98,16 +127,12 @@ public final class DigitalWellBeingToast extends LinearLayout {
final long appRemainingTimeMs =
usageLimit != null ? usageLimit.getUsageRemaining() : -1;
- post(() -> {
+ mTaskView.post(() -> {
if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
- setVisibility(GONE);
+ setNoLimit();
} else {
- setVisibility(VISIBLE);
- mText.setText(getText(appRemainingTimeMs));
+ setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
}
-
- callback.call(getContentDescriptionForTask(
- task, appUsageLimitTimeMs, appRemainingTimeMs));
});
});
}
@@ -146,7 +171,7 @@ public final class DigitalWellBeingToast extends LinearLayout {
// Use a specific string for usage less than one minute but non-zero.
if (duration.compareTo(Duration.ZERO) > 0) {
- return getResources().getString(durationLessThanOneMinuteStringId);
+ return mActivity.getString(durationLessThanOneMinuteStringId);
}
// Otherwise, return 0-minute string.
@@ -176,24 +201,24 @@ public final class DigitalWellBeingToast extends LinearLayout {
}
private String getText(long remainingTime) {
- return getResources().getString(
+ return mActivity.getString(
R.string.time_left_for_app,
getRoundedUpToMinuteReadableDuration(remainingTime));
}
- public void openAppUsageSettings() {
+ public void openAppUsageSettings(View view) {
final Intent intent = new Intent(OPEN_APP_USAGE_SETTINGS_TEMPLATE)
.putExtra(Intent.EXTRA_PACKAGE_NAME,
mTask.getTopComponent().getPackageName()).addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
try {
- final BaseActivity activity = BaseActivity.fromContext(getContext());
+ final BaseActivity activity = BaseActivity.fromContext(view.getContext());
final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(
- this, 0, 0,
- getWidth(), getHeight());
+ view, 0, 0,
+ view.getWidth(), view.getHeight());
activity.startActivity(intent, options.toBundle());
activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.APP_USAGE_SETTINGS, this);
+ LauncherLogProto.ControlType.APP_USAGE_SETTINGS, view);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to open app usage settings for task "
+ mTask.getTopComponent().getPackageName(), e);
@@ -203,7 +228,7 @@ public final class DigitalWellBeingToast extends LinearLayout {
private String getContentDescriptionForTask(
Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
return appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0 ?
- getResources().getString(
+ mActivity.getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,
getText(appRemainingTimeMs)) :
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index f66e401f9..9058e7eda 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -351,6 +351,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
.getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
setWillNotDraw(false);
updateEmptyMessage();
+
+ // Initialize quickstep specific cache params here, as this is constructed only once
+ mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
}
public OverScroller getScroller() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 694d50190..e7e41893c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -27,6 +27,8 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.res.Resources;
@@ -39,6 +41,7 @@ import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -145,14 +148,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
};
private final TaskOutlineProvider mOutlineProvider;
- private final FooterOutlineProvider mFooterOutlineProvider;
private Task mTask;
private TaskThumbnailView mSnapshotView;
private TaskMenuView mMenuView;
private IconView mIconView;
- private View mTaskFooterContainer;
- private DigitalWellBeingToast mDigitalWellBeingToast;
+ private final DigitalWellBeingToast mDigitalWellBeingToast;
private float mCurveScale;
private float mFullscreenProgress;
private final FullscreenDrawParams mCurrentFullscreenParams;
@@ -171,6 +172,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
private TaskIconCache.IconLoadRequest mIconLoadRequest;
+ // Order in which the footers appear. Lower order appear below higher order.
+ public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
+ public static final int INDEX_PROACTIVE_SUGGEST = 1;
+ private final FooterWrapper[] mFooters = new FooterWrapper[2];
+ private float mFooterVerticalOffset = 0;
+ private float mFooterAlpha = 1;
+ private int mStackHeight;
+
public TaskView(Context context) {
this(context, null);
}
@@ -208,8 +217,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
+ mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
+
mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
- mFooterOutlineProvider = new FooterOutlineProvider(mCurrentFullscreenParams);
setOutlineProvider(mOutlineProvider);
}
@@ -218,10 +228,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
- mDigitalWellBeingToast = findViewById(R.id.digital_well_being_toast);
- mTaskFooterContainer = findViewById(R.id.task_footer_container);
- mTaskFooterContainer.setOutlineProvider(mFooterOutlineProvider);
- mTaskFooterContainer.setClipToOutline(true);
}
public TaskMenuView getMenuView() {
@@ -357,15 +363,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
getRecentsView().updateLiveTileIcon(task.icon);
}
- mDigitalWellBeingToast.initialize(
- mTask,
- contentDescription -> {
- setContentDescription(contentDescription);
- if (mDigitalWellBeingToast.getVisibility() == VISIBLE
- && getRecentsView() != null) {
- getRecentsView().onDigitalWellbeingToastShown();
- }
- });
+ mDigitalWellBeingToast.initialize(mTask);
});
} else {
mSnapshotView.setThumbnail(null, null);
@@ -424,14 +422,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mIconView.setScaleX(scale);
mIconView.setScaleY(scale);
- int footerVerticalOffset = (int) (mTaskFooterContainer.getHeight() * (1.0f - scale));
- mTaskFooterContainer.setTranslationY(
- mCurrentFullscreenParams.mCurrentDrawnInsets.bottom +
- mCurrentFullscreenParams.mCurrentDrawnInsets.top +
- footerVerticalOffset);
- mFooterOutlineProvider.setFullscreenDrawParams(
- mCurrentFullscreenParams, footerVerticalOffset);
- mTaskFooterContainer.invalidateOutline();
+ mFooterVerticalOffset = 1.0f - scale;
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ footer.updateFooterOffset();
+ }
+ }
}
public void setIconScaleAnimStartProgress(float startProgress) {
@@ -505,8 +501,13 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
- float fade = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
- mTaskFooterContainer.setAlpha(fade);
+ mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ footer.mView.setAlpha(mFooterAlpha);
+ }
+ }
+
if (mMenuView != null) {
mMenuView.setPosition(getX() - getRecentsView().getScrollX(), getY());
mMenuView.setScaleX(getScaleX());
@@ -514,6 +515,56 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
+
+ /**
+ * Sets the footer at the specific index and returns the previously set footer.
+ */
+ public View setFooter(int index, View view) {
+ View oldFooter = null;
+
+ // If the footer are is already collapsed, do not animate entry
+ boolean shouldAnimateEntry = mFooterVerticalOffset <= 0;
+
+ if (mFooters[index] != null) {
+ oldFooter = mFooters[index].mView;
+ mFooters[index].release();
+ removeView(oldFooter);
+
+ // If we are replacing an existing footer, do not animate entry
+ shouldAnimateEntry = false;
+ }
+ if (view != null) {
+ int indexToAdd = getChildCount();
+ for (int i = index - 1; i >= 0; i--) {
+ if (mFooters[i] != null) {
+ indexToAdd = indexOfChild(mFooters[i].mView);
+ break;
+ }
+ }
+
+ addView(view, indexToAdd);
+ ((LayoutParams) view.getLayoutParams()).gravity =
+ Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ view.setAlpha(mFooterAlpha);
+ mFooters[index] = new FooterWrapper(view);
+ if (shouldAnimateEntry) {
+ mFooters[index].animateEntry();
+ }
+ } else {
+ mFooters[index] = null;
+ }
+
+ mStackHeight = 0;
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ footer.setVerticalShift(mStackHeight);
+ mStackHeight += footer.mExpectedHeight;
+ }
+ }
+
+ return oldFooter;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -523,6 +574,18 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
+
+ mStackHeight = 0;
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ mStackHeight += footer.mView.getHeight();
+ }
+ }
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ footer.updateFooterOffset();
+ }
+ }
}
public static float getCurveScaleForInterpolation(float linearInterpolation) {
@@ -581,26 +644,74 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
- private static final class FooterOutlineProvider extends ViewOutlineProvider {
+ private class FooterWrapper extends ViewOutlineProvider {
+
+ final View mView;
+ final ViewOutlineProvider mOldOutlineProvider;
+ final ViewOutlineProvider mDelegate;
+
+ final int mExpectedHeight;
+ final int mOldPaddingBottom;
+
+ int mAnimationOffset = 0;
+ int mEntryAnimationOffset = 0;
+
+ public FooterWrapper(View view) {
+ mView = view;
+ mOldOutlineProvider = view.getOutlineProvider();
+ mDelegate = mOldOutlineProvider == null
+ ? ViewOutlineProvider.BACKGROUND : mOldOutlineProvider;
- private FullscreenDrawParams mFullscreenDrawParams;
- private int mVerticalOffset;
- private final Rect mOutlineRect = new Rect();
+ int h = view.getLayoutParams().height;
+ if (h > 0) {
+ mExpectedHeight = h;
+ } else {
+ int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
+ view.measure(m, m);
+ mExpectedHeight = view.getMeasuredHeight();
+ }
+ mOldPaddingBottom = view.getPaddingBottom();
- FooterOutlineProvider(FullscreenDrawParams params) {
- mFullscreenDrawParams = params;
+ if (mOldOutlineProvider != null) {
+ view.setOutlineProvider(this);
+ view.setClipToOutline(true);
+ }
}
- void setFullscreenDrawParams(FullscreenDrawParams params, int verticalOffset) {
- mFullscreenDrawParams = params;
- mVerticalOffset = verticalOffset;
+ public void setVerticalShift(int shift) {
+ mView.setPadding(mView.getPaddingLeft(), mView.getPaddingTop(),
+ mView.getPaddingRight(), mOldPaddingBottom + shift);
}
@Override
public void getOutline(View view, Outline outline) {
- mOutlineRect.set(0, 0, view.getWidth(), view.getHeight());
- mOutlineRect.offset(0, -mVerticalOffset);
- outline.setRoundRect(mOutlineRect, mFullscreenDrawParams.mCurrentDrawnCornerRadius);
+ mDelegate.getOutline(view, outline);
+ outline.offset(0, -mAnimationOffset - mEntryAnimationOffset);
+ }
+
+ void updateFooterOffset() {
+ mAnimationOffset = Math.round(mStackHeight * mFooterVerticalOffset);
+ mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset
+ + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom
+ + mCurrentFullscreenParams.mCurrentDrawnInsets.top);
+ mView.invalidateOutline();
+ }
+
+ void release() {
+ mView.setOutlineProvider(mOldOutlineProvider);
+ setVerticalShift(0);
+ }
+
+ void animateEntry() {
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(anim -> {
+ float factor = 1 - anim.getAnimatedFraction();
+ int totalShift = mExpectedHeight + mView.getPaddingBottom() - mOldPaddingBottom;
+ mEntryAnimationOffset = Math.round(factor * totalShift);
+ updateFooterOffset();
+ });
+ animator.setDuration(100);
+ animator.start();
}
}
@@ -624,7 +735,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
- if (mDigitalWellBeingToast.getVisibility() == VISIBLE) {
+ if (mDigitalWellBeingToast.hasLimit()) {
info.addAction(
new AccessibilityNodeInfo.AccessibilityAction(
R.string.accessibility_app_usage_settings,
@@ -648,7 +759,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
if (action == R.string.accessibility_app_usage_settings) {
- mDigitalWellBeingToast.openAppUsageSettings();
+ mDigitalWellBeingToast.openAppUsageSettings(this);
return true;
}