From bb956bd02887f1cfc1ab0f193bc47d77f53238b4 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 8 Apr 2019 16:34:19 -0700 Subject: Add task layer drawable class for Recents Go. Add a custom drawable class that provides a hook to the caller to control a crossfade animation between two drawables. This will be helpful going forward to sync up all the animations to the controller. Note that this CL only adds the class and does not replace the current implementation. Bug: 114136250 Test: Builds Change-Id: I3cc6be79cb2ca5d64c8cc2945ff7f2ebd49632b6 --- go/quickstep/res/drawable/empty_content_box.xml | 23 ++++++ .../android/quickstep/views/TaskLayerDrawable.java | 90 ++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 go/quickstep/res/drawable/empty_content_box.xml create mode 100644 go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java diff --git a/go/quickstep/res/drawable/empty_content_box.xml b/go/quickstep/res/drawable/empty_content_box.xml new file mode 100644 index 000000000..a4883889f --- /dev/null +++ b/go/quickstep/res/drawable/empty_content_box.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java new file mode 100644 index 000000000..3a230482b --- /dev/null +++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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.quickstep.views; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; + +import androidx.annotation.NonNull; + +import com.android.launcher3.R; + +/** + * A layer drawable for task content that transitions between two drawables by crossfading. Similar + * to {@link android.graphics.drawable.TransitionDrawable} but allows callers to control transition + * progress and provides a default, empty drawable. + */ +public final class TaskLayerDrawable extends LayerDrawable { + private final Drawable mEmptyDrawable; + + public TaskLayerDrawable(Context context) { + super(new Drawable[0]); + + // Use empty drawable for both layers initially. + mEmptyDrawable = context.getResources().getDrawable( + R.drawable.empty_content_box, context.getTheme()); + addLayer(mEmptyDrawable); + addLayer(mEmptyDrawable); + setTransitionProgress(1.0f); + } + + /** + * Immediately set the front-most drawable layer. + * + * @param drawable drawable to set + */ + public void setCurrentDrawable(@NonNull Drawable drawable) { + setDrawable(0, drawable); + } + + /** + * Immediately reset the drawable to showing the empty drawable. + */ + public void resetDrawable() { + setCurrentDrawable(mEmptyDrawable); + } + + /** + * Prepare to start animating the transition by pushing the current drawable to the back and + * setting a new drawable to the front layer and making it invisible. + * + * @param endDrawable drawable to animate to + */ + public void startNewTransition(@NonNull Drawable endDrawable) { + Drawable oldDrawable = getDrawable(0); + setDrawable(1, oldDrawable); + setDrawable(0, endDrawable); + setTransitionProgress(0.0f); + } + + /** + * Set the progress of the transition animation to crossfade the two drawables. + * + * @param progress current transition progress between 0 (front view invisible) and 1 + * (front view visible) + */ + public void setTransitionProgress(float progress) { + if (progress > 1 || progress < 0) { + throw new IllegalArgumentException("Transition progress should be between 0 and 1"); + } + int drawableAlpha = (int) (progress * 255); + getDrawable(0).setAlpha(drawableAlpha); + getDrawable(1).setAlpha(255 - drawableAlpha); + invalidateSelf(); + } +} -- cgit v1.2.3 From e041ba0d6167bb041b9cf5e78ad593f3cb1cdc7c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 9 Apr 2019 15:11:53 -0700 Subject: Temporarily disable screen pinning UI in gesture nav Bug: 130225926 Change-Id: I782a5c6cdf00dfb8e501185f58defed4bfee3ac1 --- .../src/com/android/quickstep/TaskSystemShortcut.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index 42a28fbfa..6e98a5ad2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -290,6 +290,10 @@ public class TaskSystemShortcut extends SystemShortcut if (sysUiProxy == null) { return null; } + if (SysUINavigationMode.getMode(activity) == SysUINavigationMode.Mode.NO_BUTTON) { + // TODO(b/130225926): Temporarily disable pinning while gesture nav is enabled + return null; + } if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) { return null; } -- cgit v1.2.3 From 34ee30c4f92a29685a33a4bfd01f8efd10631142 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 8 Apr 2019 17:09:19 -0700 Subject: Add drawable for default thumbnail for Recents Go This CL adds a drawable for the default thumbnail view and refactors the logic to get the default icon/thumbnail/label if null to separate methods for re-use in later CLs. Bug: 114136250 Test: Manual; Go to icon recents Change-Id: I511ea40ace040fc53ffc69c27149f24d69bda7b0 --- go/quickstep/res/drawable/default_thumbnail.xml | 22 +++++++++++ .../com/android/quickstep/views/TaskItemView.java | 43 ++++++++++++---------- 2 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 go/quickstep/res/drawable/default_thumbnail.xml diff --git a/go/quickstep/res/drawable/default_thumbnail.xml b/go/quickstep/res/drawable/default_thumbnail.xml new file mode 100644 index 000000000..0a2dbf03d --- /dev/null +++ b/go/quickstep/res/drawable/default_thumbnail.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java index d831b206a..0a3fba840 100644 --- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java +++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java @@ -16,8 +16,9 @@ package com.android.quickstep.views; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; @@ -25,6 +26,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.R; @@ -36,14 +38,16 @@ public final class TaskItemView extends LinearLayout { private static final String DEFAULT_LABEL = "..."; private final Drawable mDefaultIcon; + private final Drawable mDefaultThumbnail; private TextView mLabelView; private ImageView mIconView; private ImageView mThumbnailView; public TaskItemView(Context context, AttributeSet attrs) { super(context, attrs); - mDefaultIcon = context.getResources().getDrawable( - android.R.drawable.sym_def_app_icon, context.getTheme()); + Resources res = context.getResources(); + mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme()); + mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme()); } @Override @@ -69,11 +73,7 @@ public final class TaskItemView extends LinearLayout { * @param label task label */ public void setLabel(@Nullable String label) { - if (label == null) { - mLabelView.setText(DEFAULT_LABEL); - return; - } - mLabelView.setText(label); + mLabelView.setText(getSafeLabel(label)); } /** @@ -86,11 +86,7 @@ public final class TaskItemView extends LinearLayout { // The icon proper is actually smaller than the drawable and has "padding" on the side for // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the // view if we want the icon to be flush with the bottom of the thumbnail. - if (icon == null) { - mIconView.setImageDrawable(mDefaultIcon); - return; - } - mIconView.setImageDrawable(icon); + mIconView.setImageDrawable(getSafeIcon(icon)); } /** @@ -99,16 +95,23 @@ public final class TaskItemView extends LinearLayout { * @param thumbnail task thumbnail for the task */ public void setThumbnail(@Nullable Bitmap thumbnail) { - if (thumbnail == null) { - mThumbnailView.setImageBitmap(null); - mThumbnailView.setBackgroundColor(Color.GRAY); - return; - } - mThumbnailView.setBackgroundColor(Color.TRANSPARENT); - mThumbnailView.setImageBitmap(thumbnail); + mThumbnailView.setImageDrawable(getSafeThumbnail(thumbnail)); } public View getThumbnailView() { return mThumbnailView; } + + private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) { + return (icon != null) ? icon : mDefaultIcon; + } + + private @NonNull Drawable getSafeThumbnail(@Nullable Bitmap thumbnail) { + return (thumbnail != null) ? new BitmapDrawable(getResources(), thumbnail) + : mDefaultThumbnail; + } + + private @NonNull String getSafeLabel(@Nullable String label) { + return (label != null) ? label : DEFAULT_LABEL; + } } -- cgit v1.2.3 From 29a33386ad6a1de1dced5a6693dd69e3fd7ddf3a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 10 Apr 2019 09:47:55 -0700 Subject: Check before the user is unlocked before handling proxied events Bug: 130272454 Test: None, just checking for null state Change-Id: I6b5a5c0dc7a0dbd8d2d75decd4e7d7ddf6cc3e3d --- .../src/com/android/quickstep/TouchInteractionService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 79f0c36ef..8b7009a6d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -135,6 +135,12 @@ public class TouchInteractionService extends Service implements @Override public void onAssistantVisibilityChanged(float visibility) { + if (mOverviewComponentObserver == null) { + // Save the visibility to be applied when the user is unlocked + mPendingAssistantVisibility = visibility; + return; + } + MAIN_THREAD_EXECUTOR.execute(() -> { mOverviewComponentObserver.getActivityControlHelper() .onAssistantVisibilityChanged(visibility); @@ -143,6 +149,10 @@ public class TouchInteractionService extends Service implements public void onBackAction(boolean completed, int downX, int downY, boolean isButton, boolean gestureSwipeLeft) { + if (mOverviewComponentObserver == null) { + return; + } + final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY, @@ -185,6 +195,7 @@ public class TouchInteractionService extends Service implements private InputConsumerController mInputConsumer; private SwipeSharedState mSwipeSharedState; private boolean mAssistantAvailable; + private float mPendingAssistantVisibility = 0; private boolean mIsUserUnlocked; private List mOnUserUnlockedCallbacks; @@ -337,6 +348,8 @@ public class TouchInteractionService extends Service implements mAM = ActivityManagerWrapper.getInstance(); mRecentsModel = RecentsModel.INSTANCE.get(this); mOverviewComponentObserver = new OverviewComponentObserver(this); + mOverviewComponentObserver.getActivityControlHelper().onAssistantVisibilityChanged( + mPendingAssistantVisibility); mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver); mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this); -- cgit v1.2.3 From 476a511ca5c81b2f2b813da05946226533adafbb Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 5 Apr 2019 18:35:29 -0700 Subject: Not using Settings app as a source of shortcuts Bug: 126587956 Change-Id: I13ef4298a8133ff5773eef8658a04094d5d154da --- tests/AndroidManifest-common.xml | 52 ++++++++++++---------- tests/res/values/strings.xml | 6 +++ tests/res/xml/shortcuts.xml | 18 ++++++++ .../android/launcher3/ui/TaplTestsLauncher3.java | 23 +++++----- 4 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 tests/res/values/strings.xml create mode 100644 tests/res/xml/shortcuts.xml diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 089d672b1..368649334 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -18,39 +18,39 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.launcher3.tests"> - + - + - + + android:resource="@xml/appwidget_no_config"/> - + + android:resource="@xml/appwidget_hidden"/> - + + android:resource="@xml/appwidget_with_config"/> + android:icon="@drawable/test_drawable_pin_item" + android:label="Test Pin Item"> @@ -73,28 +73,28 @@ + android:exported="true"/> + android:theme="@android:style/Theme.DeviceDefault.Light" + android:windowSoftInputMode="adjustPan"> - - - + + + - + + + + + + diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml new file mode 100644 index 000000000..0ad87fb9c --- /dev/null +++ b/tests/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Shortcut 1 + Shortcut 2 + Shortcut 3 + diff --git a/tests/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml new file mode 100644 index 000000000..bdc22f98c --- /dev/null +++ b/tests/res/xml/shortcuts.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index e11e62e79..c1727cc04 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -25,7 +25,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.content.Intent; -import android.content.pm.LauncherActivityInfo; import android.util.Log; import androidx.test.filters.LargeTest; @@ -62,6 +61,7 @@ import java.io.IOException; @RunWith(AndroidJUnit4.class) public class TaplTestsLauncher3 extends AbstractLauncherUiTest { private static final String TAG = "TaplTestsAosp"; + private static final String APP_NAME = "LauncherTestApp"; private static int sScreenshotCount = 0; @@ -307,12 +307,11 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape public void testLaunchMenuItem() throws Exception { if (!TestHelpers.isInLauncherProcess()) return; - final LauncherActivityInfo testApp = getSettingsApp(); final AppIconMenu menu = mLauncher. getWorkspace(). switchToAllApps(). - getAppIcon(testApp.getLabel().toString()). + getAppIcon(APP_NAME). openMenu(); executeOnLauncher( @@ -322,7 +321,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { final AppIconMenuItem menuItem = menu.getMenuItem(1); final String itemName = menuItem.getText(); - menuItem.launch(testApp.getComponentName().getPackageName(), itemName); + menuItem.launch(getAppPackageName(), APP_NAME); } @Test @@ -330,16 +329,15 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { public void testDragAppIcon() throws Throwable { try { TestProtocol.sDebugTracing = true; - final String appName = "LauncherTestApp"; // 1. Open all apps and wait for load complete. // 2. Drag icon to homescreen. // 3. Verify that the icon works on homescreen. mLauncher.getWorkspace(). switchToAllApps(). - getAppIcon(appName). + getAppIcon(APP_NAME). dragToWorkspace(). - getWorkspaceAppIcon(appName). - launch(getInstrumentation().getContext().getPackageName()); + getWorkspaceAppIcon(APP_NAME). + launch(getAppPackageName(), APP_NAME); } finally { TestProtocol.sDebugTracing = false; } @@ -349,7 +347,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape public void testDragShortcut() throws Throwable { if (!TestHelpers.isInLauncherProcess()) return; - LauncherActivityInfo testApp = getSettingsApp(); // 1. Open all apps and wait for load complete. // 2. Find the app and long press it to show shortcuts. @@ -357,7 +354,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { final AppIconMenuItem menuItem = mLauncher. getWorkspace(). switchToAllApps(). - getAppIcon(testApp.getLabel().toString()). + getAppIcon(APP_NAME). openMenu(). getMenuItem(0); final String shortcutName = menuItem.getText(); @@ -368,6 +365,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { menuItem. dragToWorkspace(). getWorkspaceAppIcon(shortcutName). - launch(testApp.getComponentName().getPackageName(), shortcutName); + launch(getAppPackageName(), APP_NAME); + } + + private static String getAppPackageName() { + return getInstrumentation().getContext().getPackageName(); } } -- cgit v1.2.3 From e77fc8322291225e4e1f3e87c1c98b7dccfb4815 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 11 Apr 2019 12:09:44 -0700 Subject: Use correct indicator of a long-press success in tests It's the presence of the context menu if we long-press a shortcut, and the drop target bar if we press a menu item. Change-Id: Ic02462fa766e23b74f41b8a73fefd519aea08407 --- tests/tapl/com/android/launcher3/tapl/AppIcon.java | 5 +++++ tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java | 5 +++++ tests/tapl/com/android/launcher3/tapl/Launchable.java | 7 +++++-- tests/tapl/com/android/launcher3/tapl/Workspace.java | 7 ++++--- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index fbeb3a2d0..44fc3f74f 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -50,4 +50,9 @@ public final class AppIcon extends Launchable { downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter); return new AppIconMenu(mLauncher, deepShortcutsContainer); } + + @Override + protected String getLongPressIndicator() { + return "deep_shortcuts_container"; + } } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java index c39f8d167..ba9c10e81 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java @@ -32,4 +32,9 @@ public class AppIconMenuItem extends Launchable { public String getText() { return mObject.getText(); } + + @Override + protected String getLongPressIndicator() { + return "drop_target_bar"; + } } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 16ddba855..d940412a4 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -27,7 +27,7 @@ import androidx.test.uiautomator.Until; /** * Ancestor for AppIcon and AppMenuItem. */ -class Launchable { +abstract class Launchable { protected final LauncherInstrumentation mLauncher; protected final UiObject2 mObject; @@ -76,10 +76,13 @@ class Launchable { Workspace.dragIconToWorkspace( mLauncher, this, - new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2)); + new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2), + getLongPressIndicator()); try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "dragged launchable to workspace")) { return new Workspace(mLauncher); } } + + protected abstract String getLongPressIndicator(); } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 943332e2a..46562cec6 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -118,7 +118,7 @@ public final class Workspace extends Home { mLauncher, getHotseatAppIcon("Chrome"), new Point(mLauncher.getDevice().getDisplayWidth(), - workspace.getVisibleBounds().centerY())); + workspace.getVisibleBounds().centerY()), "deep_shortcuts_container"); verifyActiveContainer(); } assertTrue("Home screen workspace didn't become scrollable", @@ -136,12 +136,13 @@ public final class Workspace extends Home { } static void dragIconToWorkspace( - LauncherInstrumentation launcher, Launchable launchable, Point dest) { + LauncherInstrumentation launcher, Launchable launchable, Point dest, + String longPressIndicator) { LauncherInstrumentation.log("dragIconToWorkspace: begin"); final Point launchableCenter = launchable.getObject().getVisibleCenter(); final long downTime = SystemClock.uptimeMillis(); launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter); - launcher.waitForLauncherObject("deep_shortcuts_container"); + launcher.waitForLauncherObject(longPressIndicator); launcher.movePointer(downTime, DRAG_DURACTION, launchableCenter, dest); launcher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest); -- cgit v1.2.3 From 77ca70fea6caf94108ecaed593d71c9db5716cff Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 11 Apr 2019 12:25:12 -0700 Subject: Tapl: also logging contexts Contexts were added for better diags when an assert happens, but they are also useful for tracing the test execution. Change-Id: I1512f3f7e02a373096bab4f0b390b1febfc96334 --- .../android/launcher3/tapl/LauncherInstrumentation.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 3a45e9342..08f2681ea 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -201,13 +201,19 @@ public final class LauncherInstrumentation { Closable addContextLayer(String piece) { mDiagnosticContext.addLast(piece); - return () -> mDiagnosticContext.removeLast(); + log("Added context: " + getContextDescription()); + return () -> { + log("Removing context: " + getContextDescription()); + mDiagnosticContext.removeLast(); + }; } private void fail(String message) { - final String ctxt = mDiagnosticContext.isEmpty() ? "" : String.join(", ", - mDiagnosticContext) + "; "; - Assert.fail("http://go/tapl : " + ctxt + message); + Assert.fail("http://go/tapl : " + getContextDescription() + message); + } + + private String getContextDescription() { + return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; "; } void assertTrue(String message, boolean condition) { -- cgit v1.2.3 From 1531982d1ed7c03cc1ec4c0a6d6069ff747de5b5 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 11 Apr 2019 11:16:06 -0700 Subject: Fix transition progress not applying to drawables TaskLayerDrawable should reapply UI changes from the transition progress when there is a new drawable. In addition, the transition should check if the drawable in the front and back are the same (i.e. on initialization when both are showing the empty drawable) so that it only applies the front-drawable alpha. Bug: 114136250 Test: Builds Change-Id: I74836b5043da555358742ba0a3f46f170f590904 --- .../src/com/android/quickstep/views/TaskLayerDrawable.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java index 3a230482b..98b66b995 100644 --- a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java +++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java @@ -31,6 +31,7 @@ import com.android.launcher3.R; */ public final class TaskLayerDrawable extends LayerDrawable { private final Drawable mEmptyDrawable; + private float mProgress; public TaskLayerDrawable(Context context) { super(new Drawable[0]); @@ -50,6 +51,7 @@ public final class TaskLayerDrawable extends LayerDrawable { */ public void setCurrentDrawable(@NonNull Drawable drawable) { setDrawable(0, drawable); + applyTransitionProgress(mProgress); } /** @@ -82,9 +84,18 @@ public final class TaskLayerDrawable extends LayerDrawable { if (progress > 1 || progress < 0) { throw new IllegalArgumentException("Transition progress should be between 0 and 1"); } + mProgress = progress; + applyTransitionProgress(progress); + } + + private void applyTransitionProgress(float progress) { int drawableAlpha = (int) (progress * 255); getDrawable(0).setAlpha(drawableAlpha); - getDrawable(1).setAlpha(255 - drawableAlpha); + if (getDrawable(0) != getDrawable(1)) { + // Only do this if it's a different drawable so that it fades out. + // Otherwise, we'd just be overwriting the front drawable's alpha. + getDrawable(1).setAlpha(255 - drawableAlpha); + } invalidateSelf(); } } -- cgit v1.2.3 From 8dc1dbe814d295c35c39c757a741dc7dec1cdcc2 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 11 Apr 2019 13:50:14 -0700 Subject: Simplifying and removing unnecessary checks from app-drag tests Change-Id: Iab6603f62b8e3c95b2c3351bfe41f35f15f59246 --- tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java | 6 +++--- tests/tapl/com/android/launcher3/tapl/Launchable.java | 13 +++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index c1727cc04..dfb0edf62 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -321,7 +321,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { final AppIconMenuItem menuItem = menu.getMenuItem(1); final String itemName = menuItem.getText(); - menuItem.launch(getAppPackageName(), APP_NAME); + menuItem.launch(getAppPackageName()); } @Test @@ -337,7 +337,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { getAppIcon(APP_NAME). dragToWorkspace(). getWorkspaceAppIcon(APP_NAME). - launch(getAppPackageName(), APP_NAME); + launch(getAppPackageName()); } finally { TestProtocol.sDebugTracing = false; } @@ -365,7 +365,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { menuItem. dragToWorkspace(). getWorkspaceAppIcon(shortcutName). - launch(getAppPackageName(), APP_NAME); + launch(getAppPackageName()); } private static String getAppPackageName() { diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index d940412a4..1b372ec13 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -45,24 +45,17 @@ abstract class Launchable { * Clicks the object to launch its app. */ public Background launch(String expectedPackageName) { - return launch(expectedPackageName, By.pkg(expectedPackageName).depth(0)); + return launch(By.pkg(expectedPackageName)); } - /** - * Clicks the object to launch its app. - */ - public Background launch(String expectedPackageName, String expectedAppText) { - return launch(expectedPackageName, By.pkg(expectedPackageName).text(expectedAppText)); - } - - private Background launch(String errorMessage, BySelector selector) { + private Background launch(BySelector selector) { LauncherInstrumentation.log("Launchable.launch before click " + mObject.getVisibleCenter()); mLauncher.assertTrue( "Launching an app didn't open a new window: " + mObject.getText(), mObject.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS)); mLauncher.assertTrue( - "App didn't start: " + errorMessage, + "App didn't start: " + selector, mLauncher.getDevice().wait(Until.hasObject(selector), LauncherInstrumentation.WAIT_TIME_MS)); return new Background(mLauncher); -- cgit v1.2.3 From 26ad999b10c35a28585ffd3a5928d90446379c95 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 10 Apr 2019 14:04:43 -0700 Subject: Change layout anim from Animation => Animator Change the layout animation to use animators instead of the built-in animation-based layout animation. Animator-based animations are more flexible and can act on the view properties themselves, making it easier to deal with if we need to cancel the animation later from a conflicting animation (i.e. we find out we need to animate a view out). Bug: 114136250 Test: Go to recents, see items animate in Change-Id: Id8227cd50e81999cac98912ac58cd2d6864c40af --- .../android/quickstep/views/IconRecentsView.java | 91 +++++++++++++++++----- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index c06b6ec41..7f77b6cc4 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -24,14 +24,12 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.View; import android.view.ViewDebug; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.LayoutAnimationController; +import android.view.ViewTreeObserver; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -40,6 +38,7 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; +import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener; import com.android.launcher3.R; import com.android.quickstep.RecentsToActivityHelper; @@ -90,7 +89,6 @@ public final class IconRecentsView extends FrameLayout { private final TaskListLoader mTaskLoader; private final TaskAdapter mTaskAdapter; private final TaskActionController mTaskActionController; - private final LayoutAnimationController mLayoutAnimation; private RecentsToActivityHelper mActivityHelper; private RecyclerView mTaskRecyclerView; @@ -98,6 +96,9 @@ public final class IconRecentsView extends FrameLayout { private View mContentView; private View mClearAllView; private boolean mTransitionedFromApp; + private AnimatorSet mLayoutAnimation; + private final ArraySet mLayingOutViews = new ArraySet<>(); + public IconRecentsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -106,7 +107,6 @@ public final class IconRecentsView extends FrameLayout { mTaskAdapter = new TaskAdapter(mTaskLoader); mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter); mTaskAdapter.setActionController(mTaskActionController); - mLayoutAnimation = createLayoutAnimation(); } @Override @@ -120,7 +120,20 @@ public final class IconRecentsView extends FrameLayout { ItemTouchHelper helper = new ItemTouchHelper( new TaskSwipeCallback(mTaskActionController)); helper.attachToRecyclerView(mTaskRecyclerView); - mTaskRecyclerView.setLayoutAnimation(mLayoutAnimation); + mTaskRecyclerView.addOnChildAttachStateChangeListener( + new OnChildAttachStateChangeListener() { + @Override + public void onChildViewAttachedToWindow(@NonNull View view) { + if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) { + // Child view was added that is not part of current layout animation + // so restart the animation. + animateFadeInLayoutAnimation(); + } + } + + @Override + public void onChildViewDetachedFromWindow(@NonNull View view) { } + }); mEmptyView = findViewById(R.id.recent_task_empty_view); mContentView = findViewById(R.id.recent_task_content_view); @@ -165,8 +178,7 @@ public final class IconRecentsView extends FrameLayout { * becomes visible. */ public void onBeginTransitionToOverview() { - mTaskRecyclerView.scheduleLayoutAnimation(); - + scheduleFadeInLayoutAnimation(); // Load any task changes if (!mTaskLoader.needsToLoad()) { return; @@ -338,17 +350,56 @@ public final class IconRecentsView extends FrameLayout { }); } - private static LayoutAnimationController createLayoutAnimation() { - AnimationSet anim = new AnimationSet(false /* shareInterpolator */); - - Animation alphaAnim = new AlphaAnimation(0, 1); - alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION); - anim.addAnimation(alphaAnim); - - LayoutAnimationController layoutAnim = new LayoutAnimationController(anim); - layoutAnim.setDelay( - (float) LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN / LAYOUT_ITEM_ANIMATE_IN_DURATION); + /** + * Schedule a one-shot layout animation on the next layout. Separate from + * {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the + * view properties themselves, allowing more controllable behavior and making it easier to + * manage when the animation conflicts with another animation. + */ + private void scheduleFadeInLayoutAnimation() { + ViewTreeObserver viewTreeObserver = mTaskRecyclerView.getViewTreeObserver(); + viewTreeObserver.addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + animateFadeInLayoutAnimation(); + viewTreeObserver.removeOnGlobalLayoutListener(this); + } + }); + } - return layoutAnim; + /** + * Start animating the layout animation where items fade in. + */ + private void animateFadeInLayoutAnimation() { + if (mLayoutAnimation != null) { + // If layout animation still in progress, cancel and restart. + mLayoutAnimation.cancel(); + } + TaskItemView[] views = getTaskViews(); + int delay = 0; + mLayoutAnimation = new AnimatorSet(); + for (TaskItemView view : views) { + view.setAlpha(0.0f); + Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f); + alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay); + alphaAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setAlpha(1.0f); + mLayingOutViews.remove(view); + } + }); + delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN; + mLayoutAnimation.play(alphaAnim); + mLayingOutViews.add(view); + } + mLayoutAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLayoutAnimation = null; + } + }); + mLayoutAnimation.start(); } } -- cgit v1.2.3 From a3d80d102e108b17adce49c440c0f4b84d3867e5 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 11 Apr 2019 13:07:46 -0700 Subject: Add task content animation property Add API to animate task item content transition from one set of task content (icon, thumbnail) to another. To do this, we provide two things to the caller: startContentAnimation which allows the caller to set the icon, thumbnail, and label to animate to and the CONTENT_TRANSITION_PROGRESS property which the caller can use to control the transition progress. We will eventually hook this up to onBindViewHolder for the task adapter when there is a data change event to prepare to animate content in. Currently it still changes immediately. Bug: 114136250 Test: Builds Change-Id: I16e9b757ee91be54fe8cba6780b399e3cc313e3e --- .../src/com/android/quickstep/TaskAdapter.java | 9 +++- .../src/com/android/quickstep/TaskHolder.java | 24 +++++++-- .../com/android/quickstep/views/TaskItemView.java | 59 +++++++++++++++++++++- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java index 674fcae91..66c074bdd 100644 --- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java +++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java @@ -94,7 +94,7 @@ public final class TaskAdapter extends Adapter { return; } Task task = tasks.get(position); - holder.bindTask(task); + holder.bindTask(task, false /* willAnimate */); mLoader.loadTaskIconAndLabel(task, () -> { // Ensure holder still has the same task. if (Objects.equals(task, holder.getTask())) { @@ -109,6 +109,13 @@ public final class TaskAdapter extends Adapter { }); } + @Override + public void onBindViewHolder(@NonNull TaskHolder holder, int position, + @NonNull List payloads) { + // TODO: Bind task in preparation for animation. For now, we apply UI changes immediately. + super.onBindViewHolder(holder, position, payloads); + } + @Override public void onViewAttachedToWindow(@NonNull TaskHolder holder) { if (holder.getTask() == null) { diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java index 98dc98931..91a3534c0 100644 --- a/go/quickstep/src/com/android/quickstep/TaskHolder.java +++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java @@ -15,6 +15,9 @@ */ package com.android.quickstep; +import android.graphics.Bitmap; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView.ViewHolder; @@ -40,13 +43,28 @@ public final class TaskHolder extends ViewHolder { } /** - * Bind a task to the holder, resetting the view and preparing it for content to load in. + * Bind the task model to the holder. This will take the current task content in the task + * object (i.e. icon, thumbnail, label) and either apply the content immediately or simply bind + * the content to animate to at a later time. If the task does not have all its content loaded, + * the view will prepare appropriate default placeholders and it is the callers responsibility + * to change them at a later time. + * + * Regardless of whether it is animating, input handlers will be bound immediately (see + * {@link TaskActionController}). * * @param task the task to bind to the view + * @param willAnimate true if UI should animate in later, false if it should apply immediately */ - public void bindTask(Task task) { + public void bindTask(@NonNull Task task, boolean willAnimate) { mTask = task; - mTaskItemView.resetTaskItemView(); + Bitmap thumbnail = (task.thumbnail != null) ? task.thumbnail.thumbnail : null; + if (willAnimate) { + mTaskItemView.startContentAnimation(task.icon, thumbnail, task.titleDescription); + } else { + mTaskItemView.setIcon(task.icon); + mTaskItemView.setThumbnail(thumbnail); + mTaskItemView.setLabel(task.titleDescription); + } } /** diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java index 0a3fba840..a8fc78a33 100644 --- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java +++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java @@ -21,6 +21,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.FloatProperty; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -39,15 +40,37 @@ public final class TaskItemView extends LinearLayout { private static final String DEFAULT_LABEL = "..."; private final Drawable mDefaultIcon; private final Drawable mDefaultThumbnail; + private final TaskLayerDrawable mIconDrawable; + private final TaskLayerDrawable mThumbnailDrawable; private TextView mLabelView; private ImageView mIconView; private ImageView mThumbnailView; + private float mContentTransitionProgress; + + /** + * Property representing the content transition progress of the view. 1.0f represents that the + * currently bound icon, thumbnail, and label are fully animated in and visible. + */ + public static FloatProperty CONTENT_TRANSITION_PROGRESS = + new FloatProperty("taskContentTransitionProgress") { + @Override + public void setValue(TaskItemView view, float progress) { + view.setContentTransitionProgress(progress); + } + + @Override + public Float get(TaskItemView view) { + return view.mContentTransitionProgress; + } + }; public TaskItemView(Context context, AttributeSet attrs) { super(context, attrs); Resources res = context.getResources(); mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme()); mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme()); + mIconDrawable = new TaskLayerDrawable(context); + mThumbnailDrawable = new TaskLayerDrawable(context); } @Override @@ -56,6 +79,12 @@ public final class TaskItemView extends LinearLayout { mLabelView = findViewById(R.id.task_label); mThumbnailView = findViewById(R.id.task_thumbnail); mIconView = findViewById(R.id.task_icon); + + mThumbnailView.setImageDrawable(mThumbnailDrawable); + mIconView.setImageDrawable(mIconDrawable); + + resetTaskItemView(); + CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f); } /** @@ -74,6 +103,7 @@ public final class TaskItemView extends LinearLayout { */ public void setLabel(@Nullable String label) { mLabelView.setText(getSafeLabel(label)); + // TODO: Animation for label } /** @@ -86,7 +116,7 @@ public final class TaskItemView extends LinearLayout { // The icon proper is actually smaller than the drawable and has "padding" on the side for // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the // view if we want the icon to be flush with the bottom of the thumbnail. - mIconView.setImageDrawable(getSafeIcon(icon)); + mIconDrawable.setCurrentDrawable(getSafeIcon(icon)); } /** @@ -95,13 +125,38 @@ public final class TaskItemView extends LinearLayout { * @param thumbnail task thumbnail for the task */ public void setThumbnail(@Nullable Bitmap thumbnail) { - mThumbnailView.setImageDrawable(getSafeThumbnail(thumbnail)); + mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnail)); } public View getThumbnailView() { return mThumbnailView; } + /** + * Start a new animation from the current task content to the specified new content. The caller + * is responsible for the actual animation control via the property + * {@link #CONTENT_TRANSITION_PROGRESS}. + * + * @param endIcon the icon to animate to + * @param endThumbnail the thumbnail to animate to + * @param endLabel the label to animate to + */ + public void startContentAnimation(@Nullable Drawable endIcon, @Nullable Bitmap endThumbnail, + @Nullable String endLabel) { + mIconDrawable.startNewTransition(getSafeIcon(endIcon)); + mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail)); + // TODO: Animation for label + + setContentTransitionProgress(0.0f); + } + + private void setContentTransitionProgress(float progress) { + mContentTransitionProgress = progress; + mIconDrawable.setTransitionProgress(progress); + mThumbnailDrawable.setTransitionProgress(progress); + // TODO: Animation for label + } + private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) { return (icon != null) ? icon : mDefaultIcon; } -- cgit v1.2.3 From a2c4200cde475cd5ab9de35945e2e8458589c39c Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 8 Apr 2019 16:35:03 -0700 Subject: Differentiate empty UI from default in recents Go Use the "empty" drawable as the initial UI until the recents task list and order is loaded, then switch to default/filled UI. Bug: 114136250 Test: Manual test; see empty UI when loading takes time Change-Id: I85b72e6d40d7224b28217cbf4e05515ec1e9451b --- go/quickstep/src/com/android/quickstep/TaskHolder.java | 5 +---- .../src/com/android/quickstep/views/TaskItemView.java | 13 +++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java index 91a3534c0..a3fa5c1f2 100644 --- a/go/quickstep/src/com/android/quickstep/TaskHolder.java +++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java @@ -73,10 +73,7 @@ public final class TaskHolder extends ViewHolder { */ public void bindEmptyUi() { mTask = null; - // TODO: Set the task view to a loading, empty UI. - // Temporarily using the one below for visual confirmation but should be swapped out to new - // UI later. - mTaskItemView.resetTaskItemView(); + mTaskItemView.resetToEmptyUi(); } /** diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java index a8fc78a33..572747bfd 100644 --- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java +++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java @@ -37,6 +37,7 @@ import com.android.launcher3.R; */ public final class TaskItemView extends LinearLayout { + private static final String EMPTY_LABEL = ""; private static final String DEFAULT_LABEL = "..."; private final Drawable mDefaultIcon; private final Drawable mDefaultThumbnail; @@ -83,17 +84,17 @@ public final class TaskItemView extends LinearLayout { mThumbnailView.setImageDrawable(mThumbnailDrawable); mIconView.setImageDrawable(mIconDrawable); - resetTaskItemView(); + resetToEmptyUi(); CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f); } /** - * Resets task item view to default values. + * Resets task item view to empty, loading UI. */ - public void resetTaskItemView() { - setLabel(DEFAULT_LABEL); - setIcon(null); - setThumbnail(null); + public void resetToEmptyUi() { + mIconDrawable.resetDrawable(); + mThumbnailDrawable.resetDrawable(); + setLabel(EMPTY_LABEL); } /** -- cgit v1.2.3 From 86957f28fff4c3b68f4ea81d48a29b42c53e8176 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 12 Apr 2019 09:57:25 -0700 Subject: Animate to bottom view in app => overview We always want to animate to the bottom so we should just do that if we have the view laid out even if at the time the app task doesn't actually match the view. Bug: 114136250 Test: Go to recents, press overview twice quickly, see it animate correctly Change-Id: I0516ef127ff6ef0f865c85314c9ffe4a7c6ef9e3 --- .../quickstep/AppToOverviewAnimationProvider.java | 7 +++--- .../src/com/android/quickstep/TaskAdapter.java | 29 ---------------------- .../android/quickstep/views/IconRecentsView.java | 11 ++++---- 3 files changed, 9 insertions(+), 38 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index d1d697c0c..c228bb94f 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -131,10 +131,11 @@ final class AppToOverviewAnimationProvider imple return anim; } - View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId); + View thumbnailView = mRecentsView.getBottomThumbnailView(); if (thumbnailView == null) { - // TODO: We should either 1) guarantee the view is loaded before attempting this - // or 2) have a backup animation. + // This can be null if there were previously 0 tasks and the recycler view has not had + // enough time to take in the data change, bind a new view, and lay out the new view. + // TODO: Have a fallback to animate to if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "No thumbnail view for running task. Using stub animation."); } diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java index 66c074bdd..02cbf4e01 100644 --- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java +++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java @@ -15,12 +15,10 @@ */ package com.android.quickstep; -import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView.Adapter; import com.android.launcher3.R; @@ -39,7 +37,6 @@ public final class TaskAdapter extends Adapter { private static final int MAX_TASKS_TO_DISPLAY = 6; private static final String TAG = "TaskAdapter"; private final TaskListLoader mLoader; - private final ArrayMap mTaskIdToViewMap = new ArrayMap<>(); private TaskActionController mTaskActionController; private boolean mIsShowingLoadingUi; @@ -63,16 +60,6 @@ public final class TaskAdapter extends Adapter { mIsShowingLoadingUi = isShowingLoadingUi; } - /** - * Get task item view for a given task id if it's attached to the view. - * - * @param taskId task id to search for - * @return corresponding task item view if it's attached, null otherwise - */ - public @Nullable TaskItemView getTaskItemView(int taskId) { - return mTaskIdToViewMap.get(taskId); - } - @Override public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) { TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext()) @@ -116,22 +103,6 @@ public final class TaskAdapter extends Adapter { super.onBindViewHolder(holder, position, payloads); } - @Override - public void onViewAttachedToWindow(@NonNull TaskHolder holder) { - if (holder.getTask() == null) { - return; - } - mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView); - } - - @Override - public void onViewDetachedFromWindow(@NonNull TaskHolder holder) { - if (holder.getTask() == null) { - return; - } - mTaskIdToViewMap.remove(holder.getTask().key.id); - } - @Override public int getItemCount() { if (mIsShowingLoadingUi) { diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 7f77b6cc4..59755bcb3 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -225,16 +225,15 @@ public final class IconRecentsView extends FrameLayout { } /** - * Get the thumbnail view associated with a task for the purposes of animation. + * Get the bottom most thumbnail view to animate to. * - * @param taskId task id of thumbnail view to get - * @return the thumbnail view for the task if attached, null otherwise + * @return the thumbnail view if laid out */ - public @Nullable View getThumbnailViewForTask(int taskId) { - TaskItemView view = mTaskAdapter.getTaskItemView(taskId); - if (view == null) { + public @Nullable View getBottomThumbnailView() { + if (mTaskRecyclerView.getChildCount() == 0) { return null; } + TaskItemView view = (TaskItemView) mTaskRecyclerView.getChildAt(0); return view.getThumbnailView(); } -- cgit v1.2.3 From e921bacfb8f8a5f594389d3d8e79eef58c25c139 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 12 Apr 2019 11:10:20 -0700 Subject: Removing hourglass_bottom Now that we don't show DWB toast for negative remaining time Bug: 127689526 Change-Id: I3ced3ec0da9bd7b09df9b66b4ef608e87339573f --- .../com/android/quickstep/views/DigitalWellBeingToast.java | 4 ---- quickstep/res/drawable/hourglass_bottom.xml | 12 ------------ quickstep/res/drawable/hourglass_top.xml | 12 ------------ quickstep/res/drawable/ic_hourglass_top.xml | 12 ++++++++++++ quickstep/res/layout/task.xml | 1 + 5 files changed, 13 insertions(+), 28 deletions(-) delete mode 100644 quickstep/res/drawable/hourglass_bottom.xml delete mode 100644 quickstep/res/drawable/hourglass_top.xml create mode 100644 quickstep/res/drawable/ic_hourglass_top.xml 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 446fb3905..b9791bfbc 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 @@ -59,7 +59,6 @@ public final class DigitalWellBeingToast extends LinearLayout { private static final String TAG = DigitalWellBeingToast.class.getSimpleName(); private Task mTask; - private ImageView mImage; private TextView mText; public DigitalWellBeingToast(Context context, AttributeSet attrs) { @@ -75,7 +74,6 @@ public final class DigitalWellBeingToast extends LinearLayout { super.onFinishInflate(); mText = findViewById(R.id.digital_well_being_remaining_time); - mImage = findViewById(R.id.digital_well_being_hourglass); } public void initialize(Task task, InitializeCallback callback) { @@ -103,8 +101,6 @@ public final class DigitalWellBeingToast extends LinearLayout { } else { setVisibility(VISIBLE); mText.setText(getText(appRemainingTimeMs)); - mImage.setImageResource(appRemainingTimeMs > 0 ? - R.drawable.hourglass_top : R.drawable.hourglass_bottom); } callback.call(getContentDescriptionForTask( diff --git a/quickstep/res/drawable/hourglass_bottom.xml b/quickstep/res/drawable/hourglass_bottom.xml deleted file mode 100644 index b5ef008d2..000000000 --- a/quickstep/res/drawable/hourglass_bottom.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/quickstep/res/drawable/hourglass_top.xml b/quickstep/res/drawable/hourglass_top.xml deleted file mode 100644 index 7fc77d3fc..000000000 --- a/quickstep/res/drawable/hourglass_top.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/quickstep/res/drawable/ic_hourglass_top.xml b/quickstep/res/drawable/ic_hourglass_top.xml new file mode 100644 index 000000000..7fc77d3fc --- /dev/null +++ b/quickstep/res/drawable/ic_hourglass_top.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index f96a66fc7..1d1c2723f 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -49,6 +49,7 @@ android:layout_width="24dp" android:layout_height="24dp" android:layout_marginEnd="8dp" + android:src="@drawable/ic_hourglass_top" /> Date: Thu, 11 Apr 2019 16:40:29 -0700 Subject: Animate content fill animation to Recents Go This CL adds the animation that occurs when transitioning from a set of empty views to the actual task list after it is loaded. This is done by setting a one-shot item animator that animates changes, for item views that fade from empty to filled, and removes, for when we have too many empty views. Bug: 114136250 Test: Artificially increase task list load time and see animation fill Change-Id: Ibbc09db702e591063ceea61df2359f18a3fcf8f9 --- .../android/quickstep/ContentFillItemAnimator.java | 276 +++++++++++++++++++++ .../src/com/android/quickstep/TaskAdapter.java | 33 ++- .../android/quickstep/views/IconRecentsView.java | 24 +- 3 files changed, 323 insertions(+), 10 deletions(-) create mode 100644 go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java new file mode 100644 index 000000000..1b6f2e34d --- /dev/null +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2019 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.quickstep; + +import static android.view.View.ALPHA; + +import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT; +import static com.android.quickstep.views.TaskItemView.CONTENT_TRANSITION_PROGRESS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; +import androidx.recyclerview.widget.SimpleItemAnimator; + +import com.android.quickstep.views.TaskItemView; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * An item animator that is only set and used for the transition from the empty loading UI to + * the filled task content UI. The animation starts from the bottom to top, changing all valid + * empty item views to be filled and removing all extra empty views. + */ +public final class ContentFillItemAnimator extends SimpleItemAnimator { + + private static final class PendingAnimation { + ViewHolder viewHolder; + int animType; + + PendingAnimation(ViewHolder vh, int type) { + viewHolder = vh; + animType = type; + } + } + + private static final int ANIM_TYPE_REMOVE = 0; + private static final int ANIM_TYPE_CHANGE = 1; + + private static final int ITEM_BETWEEN_DELAY = 40; + private static final int ITEM_CHANGE_DURATION = 150; + private static final int ITEM_REMOVE_DURATION = 150; + + /** + * Animations that have been registered to occur together at the next call of + * {@link #runPendingAnimations()} but have not started. + */ + private final ArrayList mPendingAnims = new ArrayList<>(); + + /** + * Animations that have started and are running. + */ + private final ArrayList mRunningAnims = new ArrayList<>(); + + private Runnable mOnFinishRunnable; + + /** + * Set runnable to run after the content fill animation is fully completed. + * + * @param runnable runnable to run on end + */ + public void setOnAnimationFinishedRunnable(Runnable runnable) { + mOnFinishRunnable = runnable; + } + + @Override + public void setChangeDuration(long changeDuration) { + throw new UnsupportedOperationException("Cascading item animator cannot have animation " + + "duration changed."); + } + + @Override + public void setRemoveDuration(long removeDuration) { + throw new UnsupportedOperationException("Cascading item animator cannot have animation " + + "duration changed."); + } + + @Override + public boolean animateRemove(ViewHolder holder) { + PendingAnimation pendAnim = new PendingAnimation(holder, ANIM_TYPE_REMOVE); + mPendingAnims.add(pendAnim); + return true; + } + + private void animateRemoveImpl(ViewHolder holder, long startDelay) { + final View view = holder.itemView; + if (holder.itemView.getAlpha() == 0) { + // View is already visually removed. We can just get rid of it now. + view.setAlpha(1.0f); + dispatchRemoveFinished(holder); + dispatchFinishedWhenDone(); + return; + } + final ObjectAnimator anim = ObjectAnimator.ofFloat( + holder.itemView, ALPHA, holder.itemView.getAlpha(), 0.0f); + anim.setDuration(ITEM_REMOVE_DURATION).setStartDelay(startDelay); + anim.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(Animator animation) { + view.setAlpha(1); + dispatchRemoveFinished(holder); + mRunningAnims.remove(anim); + dispatchFinishedWhenDone(); + } + } + ); + anim.start(); + mRunningAnims.add(anim); + } + + @Override + public boolean animateAdd(ViewHolder holder) { + dispatchAddFinished(holder); + return false; + } + + @Override + public boolean animateMove(ViewHolder holder, int fromX, int fromY, int toX, + int toY) { + dispatchMoveFinished(holder); + return false; + } + + @Override + public boolean animateChange(ViewHolder oldHolder, + ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) { + // Only support changes where the holders are the same + if (oldHolder == newHolder) { + PendingAnimation pendAnim = new PendingAnimation(oldHolder, ANIM_TYPE_CHANGE); + mPendingAnims.add(pendAnim); + return true; + } + dispatchChangeFinished(oldHolder, true /* oldItem */); + dispatchChangeFinished(newHolder, false /* oldItem */); + return false; + } + + private void animateChangeImpl(ViewHolder viewHolder, long startDelay) { + TaskItemView itemView = (TaskItemView) viewHolder.itemView; + final ObjectAnimator anim = + ObjectAnimator.ofFloat(itemView, CONTENT_TRANSITION_PROGRESS, 0.0f, 1.0f); + anim.setDuration(ITEM_CHANGE_DURATION).setStartDelay(startDelay); + anim.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + dispatchChangeStarting(viewHolder, true /* oldItem */); + } + + @Override + public void onAnimationEnd(Animator animation) { + dispatchChangeFinished(viewHolder, true /* oldItem */); + mRunningAnims.remove(anim); + dispatchFinishedWhenDone(); + } + } + ); + anim.start(); + mRunningAnims.add(anim); + } + + @Override + public void runPendingAnimations() { + // Run animations bottom to top. + mPendingAnims.sort(Comparator.comparingInt(o -> -o.viewHolder.itemView.getBottom())); + int delay = 0; + while (!mPendingAnims.isEmpty()) { + PendingAnimation curAnim = mPendingAnims.remove(0); + ViewHolder vh = curAnim.viewHolder; + switch (curAnim.animType) { + case ANIM_TYPE_REMOVE: + animateRemoveImpl(vh, delay); + break; + case ANIM_TYPE_CHANGE: + animateChangeImpl(vh, delay); + break; + default: + break; + } + delay += ITEM_BETWEEN_DELAY; + } + } + + @Override + public void endAnimation(@NonNull ViewHolder item) { + for (int i = mPendingAnims.size() - 1; i >= 0; i--) { + PendingAnimation pendAnim = mPendingAnims.get(i); + if (pendAnim.viewHolder == item) { + mPendingAnims.remove(i); + switch (pendAnim.animType) { + case ANIM_TYPE_REMOVE: + dispatchRemoveFinished(item); + break; + case ANIM_TYPE_CHANGE: + dispatchChangeFinished(item, true /* oldItem */); + break; + default: + break; + } + } + } + dispatchFinishedWhenDone(); + } + + @Override + public void endAnimations() { + for (int i = mPendingAnims.size() - 1; i >= 0; i--) { + PendingAnimation pendAnim = mPendingAnims.get(i); + ViewHolder item = pendAnim.viewHolder; + switch (pendAnim.animType) { + case ANIM_TYPE_REMOVE: + dispatchRemoveFinished(item); + break; + case ANIM_TYPE_CHANGE: + dispatchChangeFinished(item, true /* oldItem */); + break; + default: + break; + } + mPendingAnims.remove(i); + } + for (int i = 0; i < mRunningAnims.size(); i++) { + ObjectAnimator anim = mRunningAnims.get(i); + anim.end(); + } + dispatchAnimationsFinished(); + } + + @Override + public boolean isRunning() { + return !mPendingAnims.isEmpty() || !mRunningAnims.isEmpty(); + } + + @Override + public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder, + @NonNull List payloads) { + if (!payloads.isEmpty() + && (int) payloads.get(0) == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) { + return true; + } + return super.canReuseUpdatedViewHolder(viewHolder, payloads); + } + + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + if (mOnFinishRunnable != null) { + mOnFinishRunnable.run(); + } + } + } +} diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java index 02cbf4e01..5e0e8ff8b 100644 --- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java +++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java @@ -34,6 +34,8 @@ import java.util.Objects; */ public final class TaskAdapter extends Adapter { + public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0; + private static final int MAX_TASKS_TO_DISPLAY = 6; private static final String TAG = "TaskAdapter"; private final TaskListLoader mLoader; @@ -71,6 +73,28 @@ public final class TaskAdapter extends Adapter { @Override public void onBindViewHolder(TaskHolder holder, int position) { + onBindViewHolderInternal(holder, position, false /* willAnimate */); + } + + @Override + public void onBindViewHolder(@NonNull TaskHolder holder, int position, + @NonNull List payloads) { + if (payloads.isEmpty()) { + super.onBindViewHolder(holder, position, payloads); + return; + } + int changeType = (int) payloads.get(0); + if (changeType == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) { + // Bind in preparation for animation + onBindViewHolderInternal(holder, position, true /* willAnimate */); + } else { + throw new IllegalArgumentException("Payload content is not a valid change event type: " + + changeType); + } + } + + private void onBindViewHolderInternal(@NonNull TaskHolder holder, int position, + boolean willAnimate) { if (mIsShowingLoadingUi) { holder.bindEmptyUi(); return; @@ -81,7 +105,7 @@ public final class TaskAdapter extends Adapter { return; } Task task = tasks.get(position); - holder.bindTask(task, false /* willAnimate */); + holder.bindTask(task, willAnimate /* willAnimate */); mLoader.loadTaskIconAndLabel(task, () -> { // Ensure holder still has the same task. if (Objects.equals(task, holder.getTask())) { @@ -96,13 +120,6 @@ public final class TaskAdapter extends Adapter { }); } - @Override - public void onBindViewHolder(@NonNull TaskHolder holder, int position, - @NonNull List payloads) { - // TODO: Bind task in preparation for animation. For now, we apply UI changes immediately. - super.onBindViewHolder(holder, position, payloads); - } - @Override public int getItemCount() { if (mIsShowingLoadingUi) { diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 59755bcb3..41f25105c 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -17,6 +17,8 @@ package com.android.quickstep.views; import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL; +import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -34,6 +36,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -41,6 +44,7 @@ import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener; import com.android.launcher3.R; +import com.android.quickstep.ContentFillItemAnimator; import com.android.quickstep.RecentsToActivityHelper; import com.android.quickstep.TaskActionController; import com.android.quickstep.TaskAdapter; @@ -89,6 +93,9 @@ public final class IconRecentsView extends FrameLayout { private final TaskListLoader mTaskLoader; private final TaskAdapter mTaskAdapter; private final TaskActionController mTaskActionController; + private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator(); + private final ContentFillItemAnimator mLoadingContentItemAnimator = + new ContentFillItemAnimator(); private RecentsToActivityHelper mActivityHelper; private RecyclerView mTaskRecyclerView; @@ -134,6 +141,9 @@ public final class IconRecentsView extends FrameLayout { @Override public void onChildViewDetachedFromWindow(@NonNull View view) { } }); + mTaskRecyclerView.setItemAnimator(mDefaultItemAnimator); + mLoadingContentItemAnimator.setOnAnimationFinishedRunnable( + () -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator())); mEmptyView = findViewById(R.id.recent_task_empty_view); mContentView = findViewById(R.id.recent_task_content_view); @@ -186,9 +196,19 @@ public final class IconRecentsView extends FrameLayout { mTaskAdapter.setIsShowingLoadingUi(true); mTaskAdapter.notifyDataSetChanged(); mTaskLoader.loadTaskList(tasks -> { + int numEmptyItems = mTaskAdapter.getItemCount(); mTaskAdapter.setIsShowingLoadingUi(false); - // TODO: Animate the loading UI out and the loaded data in. - mTaskAdapter.notifyDataSetChanged(); + int numActualItems = mTaskAdapter.getItemCount(); + if (numEmptyItems < numActualItems) { + throw new IllegalStateException("There are less empty item views than the number " + + "of items to animate to."); + } + // Set item animator for content filling animation. The item animator will switch back + // to the default on completion. + mTaskRecyclerView.setItemAnimator(mLoadingContentItemAnimator); + mTaskAdapter.notifyItemRangeRemoved(numActualItems, numEmptyItems - numActualItems); + mTaskAdapter.notifyItemRangeChanged( + 0, numActualItems, CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT); }); } -- cgit v1.2.3 From f1a47a0fe6e3b6f2071f8a30ac24d7522040d76c Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 12 Apr 2019 15:41:49 -0700 Subject: Update Go recents visibility based off adapter View visibility should be based off adapter since that's what the layout items are based off of. Bug: 130440957 Test: Loading UI is shown when going to recents Go Change-Id: I1f167553b6fdce757865c739b9793b63a20e7f57 --- go/quickstep/src/com/android/quickstep/views/IconRecentsView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 41f25105c..47244068c 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -334,7 +334,7 @@ public final class IconRecentsView extends FrameLayout { * of tasks. */ private void updateContentViewVisibility() { - int taskListSize = mTaskLoader.getCurrentTaskList().size(); + int taskListSize = mTaskAdapter.getItemCount(); if (mEmptyView.getVisibility() != VISIBLE && taskListSize == 0) { crossfadeViews(mEmptyView, mContentView); mActivityHelper.leaveRecents(); -- cgit v1.2.3 From c06522c82661501cd09c305f51f0e2b07e904563 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 15 Apr 2019 16:21:59 -0700 Subject: Change layout size to be dependent on device size This CL changes the layout sizes to be dependent on device height as opposed to static values so that it works on different decides out of the box. Bug: 114136250 Test: Tested how layout looks on marlin_svelte and walleye Change-Id: Ie000bc797d7dd2e38cd705d54f3e09c79e1a2176 --- go/quickstep/res/layout/icon_recents_root_view.xml | 5 +- go/quickstep/res/layout/task_item_view.xml | 15 +++-- go/quickstep/res/values/dimens.xml | 26 --------- .../src/com/android/quickstep/TaskAdapter.java | 17 +++++- .../android/quickstep/views/IconRecentsView.java | 20 ++++++- .../com/android/quickstep/views/TaskItemView.java | 32 +++++++++++ .../android/quickstep/views/TaskLayoutUtils.java | 66 ++++++++++++++++++++++ 7 files changed, 141 insertions(+), 40 deletions(-) delete mode 100644 go/quickstep/res/values/dimens.xml create mode 100644 go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml index fddb1d347..630088236 100644 --- a/go/quickstep/res/layout/icon_recents_root_view.xml +++ b/go/quickstep/res/layout/icon_recents_root_view.xml @@ -33,10 +33,9 @@ android:scrollbars="none"/> diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml index 6dc293f74..6fb7e19d1 100644 --- a/go/quickstep/res/layout/icon_recents_root_view.xml +++ b/go/quickstep/res/layout/icon_recents_root_view.xml @@ -19,29 +19,11 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - -