Snap for 7464903 from d2ad97989c to sc-release

Change-Id: Idb65589542d289db602b4194e5a8c607cb1e9c29
This commit is contained in:
android-build-team Robot
2021-06-17 01:09:21 +00:00
42 changed files with 672 additions and 322 deletions
@@ -422,8 +422,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
return Stream.concat(super.getSupportedShortcuts(),
Stream.of(WellbeingModel.SHORTCUT_FACTORY));
return Stream.concat(Stream.of(WellbeingModel.SHORTCUT_FACTORY),
super.getSupportedShortcuts());
}
@Override
@@ -192,7 +192,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
return Stream.concat(
super.getSupportedShortcuts(), Stream.of(mHotseatPredictionController));
Stream.of(mHotseatPredictionController), super.getSupportedShortcuts());
}
/**
@@ -45,8 +45,6 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END;
import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -248,7 +246,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private static final long SWIPE_PIP_TO_HOME_DURATION = 425;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
protected boolean mIsSwipingPipToHome;
@@ -1134,17 +1131,14 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
final RectFSpringAnim windowAnim;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
mSwipePipToHomeAnimator = createWindowAnimationToPip(
homeAnimFactory, runningTaskTarget, start);
mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
mSwipePipToHomeAnimator.setInterpolator(interpolator);
mSwipePipToHomeAnimator.setFloatValues(FRACTION_START, FRACTION_END);
mSwipePipToHomeAnimator.start();
mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
windowAnim = mSwipePipToHomeAnimator;
} else {
mSwipePipToHomeAnimator = null;
RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
@@ -1158,9 +1152,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
windowAnim.start(mContext, velocityPxPerMs);
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
}
windowAnim.start(mContext, velocityPxPerMs);
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
mLauncherTransitionController = null;
@@ -1216,7 +1210,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
}
private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
@@ -1229,16 +1223,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
mDp.hotseatBarSizePx);
final Rect startBounds = new Rect();
updateProgressForStartRect(new Matrix(), startProgress).round(startBounds);
final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
mContext,
runningTaskTarget.taskId,
taskInfo.topActivity,
runningTaskTarget.leash.getSurfaceControl(),
TaskInfoCompat.getPipSourceRectHint(
runningTaskTarget.taskInfo.pictureInPictureParams),
TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
startBounds,
updateProgressForStartRect(new Matrix(), startProgress),
destinationBounds,
mRecentsView.getPipCornerRadius(),
mRecentsView);
@@ -1250,7 +1243,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
AnimatorPlaybackController activityAnimationToHome =
homeAnimFactory.createActivityAnimationToHome();
swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
swipePipToHomeAnimator.addAnimatorListener(new AnimatorListenerAdapter() {
private boolean mHasAnimationEnded;
@Override
public void onAnimationStart(Animator animation) {
@@ -27,8 +27,11 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_ENABLED;
import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
import android.content.Context;
import android.content.SharedPreferences;
@@ -40,6 +43,7 @@ import android.util.Xml;
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
@@ -170,6 +174,13 @@ public class SettingsChangeLogger implements
if (gridSizeChangedEvent != null) {
logger.log(gridSizeChangedEvent);
}
if (FeatureFlags.ENABLE_THEMED_ICONS.get()) {
logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
? LAUNCHER_THEMED_ICON_ENABLED
: LAUNCHER_THEMED_ICON_DISABLED);
}
mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
}
@@ -20,8 +20,8 @@ import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.C
import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -44,30 +44,24 @@ import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
/**
* An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when
* swiping up (in gesture navigation mode). Note that this class is derived from
* {@link com.android.wm.shell.pip.PipAnimationController.PipTransitionAnimator}.
*
* TODO: consider sharing this class including the animator and leash operations between
* Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
* PiP window, which would ideally be on SysUI side as well.
* Subclass of {@link RectFSpringAnim} that animates an Activity to PiP (picture-in-picture) window
* when swiping up (in gesture navigation mode).
*/
public class SwipePipToHomeAnimator extends ValueAnimator {
public class SwipePipToHomeAnimator extends RectFSpringAnim {
private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
public static final float FRACTION_START = 0f;
public static final float FRACTION_END = 1f;
private static final float END_PROGRESS = 1.0f;
private final int mTaskId;
private final ComponentName mComponentName;
private final SurfaceControl mLeash;
private final Rect mAppBounds = new Rect();
private final Rect mStartBounds = new Rect();
private final Rect mCurrentBounds = new Rect();
private final Rect mDestinationBounds = new Rect();
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
/** for calculating the transform in {@link #onAnimationUpdate(ValueAnimator)} */
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
/** for calculating transform in {@link #onAnimationUpdate(AppCloseConfig, RectF, float)} */
private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
private final Rect mSourceHintRectInsets;
private final Rect mSourceInsets = new Rect();
@@ -90,6 +84,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
private SurfaceControl mContentOverlay;
/**
* @param context {@link Context} provides Launcher resources
* @param taskId Task id associated with this animator, see also {@link #getTaskId()}
* @param componentName Component associated with this animator,
* see also {@link #getComponentName()}
@@ -102,20 +97,22 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
* @param destinationBounds Bounds of the destination this animator ends to
* @param cornerRadius Corner radius in pixel value for PiP window
*/
public SwipePipToHomeAnimator(int taskId,
public SwipePipToHomeAnimator(@NonNull Context context,
int taskId,
@NonNull ComponentName componentName,
@NonNull SurfaceControl leash,
@Nullable Rect sourceRectHint,
@NonNull Rect appBounds,
@NonNull Rect startBounds,
@NonNull RectF startBounds,
@NonNull Rect destinationBounds,
int cornerRadius,
@NonNull View view) {
super(startBounds, new RectF(destinationBounds), context);
mTaskId = taskId;
mComponentName = componentName;
mLeash = leash;
mAppBounds.set(appBounds);
mStartBounds.set(startBounds);
startBounds.round(mStartBounds);
mDestinationBounds.set(destinationBounds);
mDestinationBoundsTransformed.set(mDestinationBounds);
mDestinationBoundsAnimation.set(mDestinationBounds);
@@ -151,10 +148,10 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
t.reparent(mContentOverlay, mLeash);
t.apply();
addUpdateListener(valueAnimator -> {
float alpha = valueAnimator.getAnimatedFraction() < 0.5f
addOnUpdateListener((values, currentRect, progress) -> {
float alpha = progress < 0.5f
? 0
: Utilities.mapToRange(valueAnimator.getAnimatedFraction(), 0.5f, 1f,
: Utilities.mapToRange(Math.min(progress, 1f), 0.5f, 1f,
0f, 1f, Interpolators.FAST_OUT_SLOW_IN);
t.setAlpha(mContentOverlay, alpha);
t.apply();
@@ -166,7 +163,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
appBounds.bottom - sourceRectHint.bottom);
}
addListener(new AnimationSuccessListener() {
addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
InteractionJankMonitorWrapper.begin(view, CUJ_APP_CLOSE_TO_PIP);
@@ -191,7 +188,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
mHasAnimationEnded = true;
}
});
addUpdateListener(this::onAnimationUpdate);
addOnUpdateListener(this::onAnimationUpdate);
}
/** sets the from rotation if it's different from the target rotation. */
@@ -219,34 +216,34 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
mAppBounds.top + mDestinationBounds.height());
}
private void onAnimationUpdate(ValueAnimator animator) {
private void onAnimationUpdate(@Nullable AppCloseConfig values, RectF currentRect,
float progress) {
if (mHasAnimationEnded) return;
final SurfaceControl.Transaction tx =
PipSurfaceTransactionHelper.newSurfaceControlTransaction();
onAnimationUpdate(tx, animator.getAnimatedFraction());
onAnimationUpdate(tx, currentRect, progress);
tx.apply();
}
private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx,
float fraction) {
final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
mDestinationBoundsAnimation);
RectF currentRect, float progress) {
currentRect.round(mCurrentBounds);
final PictureInPictureSurfaceTransaction op;
if (mSourceHintRectInsets == null) {
// no source rect hint been set, directly scale the window down
op = onAnimationScale(fraction, tx, bounds);
op = onAnimationScale(progress, tx, mCurrentBounds);
} else {
// scale and crop according to the source rect hint
op = onAnimationScaleAndCrop(fraction, tx, bounds);
op = onAnimationScaleAndCrop(progress, tx, mCurrentBounds);
}
return op;
}
/** scale the window directly with no source rect hint being set */
private PictureInPictureSurfaceTransaction onAnimationScale(
float fraction, SurfaceControl.Transaction tx, Rect bounds) {
float progress, SurfaceControl.Transaction tx, Rect bounds) {
if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
final RotatedPosition rotatedPosition = getRotatedPosition(progress);
return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
} else {
@@ -256,12 +253,12 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
/** scale and crop the window with source rect hint */
private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop(
float fraction, SurfaceControl.Transaction tx,
float progress, SurfaceControl.Transaction tx,
Rect bounds) {
final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
final Rect insets = mInsetsEvaluator.evaluate(progress, mSourceInsets,
mSourceHintRectInsets);
if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
final RotatedPosition rotatedPosition = getRotatedPosition(progress);
return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
} else {
@@ -291,22 +288,22 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
// get the final leash operations but do not apply to the leash.
final SurfaceControl.Transaction tx =
PipSurfaceTransactionHelper.newSurfaceControlTransaction();
return onAnimationUpdate(tx, FRACTION_END);
return onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS);
}
private RotatedPosition getRotatedPosition(float fraction) {
private RotatedPosition getRotatedPosition(float progress) {
final float degree, positionX, positionY;
if (mFromRotation == Surface.ROTATION_90) {
degree = -90 * fraction;
positionX = fraction * (mDestinationBoundsTransformed.left - mStartBounds.left)
degree = -90 * progress;
positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left)
+ mStartBounds.left;
positionY = fraction * (mDestinationBoundsTransformed.bottom - mStartBounds.top)
positionY = progress * (mDestinationBoundsTransformed.bottom - mStartBounds.top)
+ mStartBounds.top;
} else {
degree = 90 * fraction;
positionX = fraction * (mDestinationBoundsTransformed.right - mStartBounds.left)
degree = 90 * progress;
positionX = progress * (mDestinationBoundsTransformed.right - mStartBounds.left)
+ mStartBounds.left;
positionY = fraction * (mDestinationBoundsTransformed.top - mStartBounds.top)
positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top)
+ mStartBounds.top;
}
return new RotatedPosition(degree, positionX, positionY);
+2 -2
View File
@@ -16,8 +16,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportHeight="20.0"
android:viewportWidth="20.0"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
android:tint="?android:attr/textColorPrimary">
<path
android:fillColor="@android:color/white"
+1 -1
View File
@@ -19,7 +19,7 @@
android:height="@dimen/deep_shortcut_drag_handle_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/textColorHint" >
android:tint="?android:attr/textColorPrimary" >
<path
android:pathData="M20,9H4v2h16V9z M4,15h16v-2H4V15z"
+2 -2
View File
@@ -16,8 +16,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20.0"
android:viewportHeight="20.0"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/textColorPrimary" >
<path
android:fillColor="@android:color/white"
+1 -1
View File
@@ -45,6 +45,6 @@
android:layout_height="@dimen/system_shortcut_icon_size"
android:layout_marginStart="@dimen/system_shortcut_margin_start"
android:layout_gravity="start|center_vertical"
android:backgroundTint="?android:attr/textColorTertiary"/>
android:backgroundTint="?android:attr/textColorPrimary"/>
</com.android.launcher3.shortcuts.DeepShortcutView>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_shortcut_full"
android:layout_width="match_parent"
android:layout_height="@dimen/system_shortcut_header_height"
android:orientation="horizontal"
android:gravity="end|center_vertical"
android:elevation="@dimen/deep_shortcuts_elevation"
android:tag="@string/popup_container_iterate_children"
android:clipToPadding="true">
</LinearLayout>
+1 -1
View File
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<TableLayout
<com.android.launcher3.widget.picker.WidgetsListTableView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widgets_table"
android:layout_width="match_parent"
+30 -22
View File
@@ -16,30 +16,38 @@
<com.android.launcher3.allapps.WorkEduCard xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/work_edu_card_margin"
android:orientation="vertical"
android:background="@drawable/work_card"
android:gravity="center">
<TextView
style="@style/PrimaryHeadline"
android:textColor="?android:attr/textColorPrimary"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@string/work_profile_edu_work_apps"
android:textAlignment="center"
android:textSize="20sp" />
android:orientation="vertical"
android:padding="@dimen/work_edu_card_margin"
android:background="@drawable/work_card"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:id="@+id/wrapper">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/action_btn"
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_profile_edu_accept"
android:textAlignment="center"
android:background="@drawable/work_card_btn"
android:textSize="14sp" />
<TextView
style="@style/PrimaryHeadline"
android:textColor="?android:attr/textColorPrimary"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@string/work_profile_edu_work_apps"
android:textAlignment="center"
android:textSize="20sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/action_btn"
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_profile_edu_accept"
android:textAlignment="center"
android:background="@drawable/work_card_btn"
android:textSize="14sp" />
</LinearLayout>
</com.android.launcher3.allapps.WorkEduCard>
+15 -5
View File
@@ -12,11 +12,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.android.launcher3.allapps.WorkPausedCard xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/allAppsScrimColor"
android:padding="48dp"
android:layout_height="wrap_content"
android:padding="@dimen/work_edu_card_margin"
android:orientation="vertical"
android:gravity="center">
@@ -39,5 +38,16 @@
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
android:layout_marginBottom="8dp"
android:textSize="16sp" />
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/enable_work_apps"
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_enable_btn_text"
android:textAlignment="center"
android:background="@drawable/work_card_btn"
android:textSize="14sp" />
</com.android.launcher3.allapps.WorkPausedCard>
+2 -3
View File
@@ -94,11 +94,11 @@
<dimen name="all_apps_background_canvas_height">475dp</dimen>
<dimen name="all_apps_header_pill_height">48dp</dimen>
<dimen name="all_apps_header_pill_corner_radius">18dp</dimen>
<dimen name="all_apps_header_pills_width">320dp</dimen>
<dimen name="all_apps_header_pills_width">348dp</dimen>
<dimen name="all_apps_header_tab_height">48dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
<dimen name="all_apps_header_top_padding">36dp</dimen>
<dimen name="all_apps_header_bottom_padding">16dp</dimen>
<dimen name="all_apps_header_bottom_padding">6dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
@@ -140,7 +140,6 @@
<dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
<dimen name="widget_list_content_corner_radius">4dp</dimen>
<dimen name="widget_list_content_joined_corner_radius">0dp</dimen>
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_bottom_margin">2dp</dimen>
@@ -107,7 +107,10 @@ public final class WidgetsListHeaderViewHolderBinderTest {
/* iconClickListener= */ view -> {},
/* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
LayoutInflater.from(mTestActivity),
mOnHeaderClickListener,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter);
}
@After
@@ -107,7 +107,10 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest {
/* iconClickListener= */ view -> {},
/* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
LayoutInflater.from(mTestActivity),
mOnHeaderClickListener,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter);
}
@After
@@ -118,6 +118,7 @@ public final class WidgetsListTableViewHolderBinderTest {
mOnIconClickListener,
mOnLongClickListener,
mWidgetPreviewLoader,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter);
}
+12 -11
View File
@@ -36,8 +36,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.widget.PopupWindow;
import android.widget.TextView;
import androidx.appcompat.content.res.AppCompatResources;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
@@ -82,6 +80,8 @@ public abstract class ButtonDropTarget extends TextView
private boolean mAccessibleDrag;
/** An item must be dragged at least this many pixels before this drop target is enabled. */
private final int mDragDistanceThreshold;
/** The size of the drawable shown in the drop target. */
private final int mDrawableSize;
protected CharSequence mText;
protected ColorStateList mOriginalTextColor;
@@ -103,6 +103,7 @@ public abstract class ButtonDropTarget extends TextView
Resources resources = getResources();
mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
}
@Override
@@ -120,14 +121,18 @@ public abstract class ButtonDropTarget extends TextView
}
protected void setDrawable(int resId) {
mDrawable = getContext().getDrawable(resId).mutate();
mDrawable.setBounds(0, 0, mDrawableSize, mDrawableSize);
setDrawable(mDrawable);
}
private void setDrawable(Drawable drawable) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
if (mTextVisible) {
setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
mDrawable = getCompoundDrawablesRelative()[0];
setCompoundDrawablesRelative(drawable, null, null, null);
} else {
setCompoundDrawablesRelativeWithIntrinsicBounds(0, resId, 0, 0);
mDrawable = getCompoundDrawablesRelative()[1];
setCompoundDrawablesRelative(null, drawable, null, null);
}
}
@@ -329,11 +334,7 @@ public abstract class ButtonDropTarget extends TextView
if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
mTextVisible = isVisible;
setText(newText);
if (mTextVisible) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
} else {
setCompoundDrawablesRelativeWithIntrinsicBounds(null, mDrawable, null, null);
}
setDrawable(mDrawable);
}
}
@@ -21,9 +21,10 @@ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCU
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -82,10 +83,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
ScrimView.ScrimDrawingController {
public static final float PULL_MULTIPLIER = .02f;
public static final float FLING_VELOCITY_MULTIPLIER = 2000f;
// Starts the springs after at least 25% of the animation has passed.
public static final float FLING_ANIMATION_THRESHOLD = 0.25f;
public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -494,15 +492,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
}
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
View overlay = mAH[AdapterHolder.WORK].getOverlayView();
int v = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? GONE : VISIBLE;
overlay.findViewById(R.id.work_apps_paused_title).setVisibility(v);
overlay.findViewById(R.id.work_apps_paused_content).setVisibility(v);
}
private void replaceRVContainer(boolean showTabs) {
for (int i = 0; i < mAH.length; i++) {
if (mAH[i].recyclerView != null) {
@@ -544,9 +533,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
&& mAllAppsStore.hasModelFlag(
FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
}
if (mSearchUiManager != null && mSearchUiManager.getEditText() != null) {
mSearchUiManager.getEditText().hideKeyboard();
}
}
// Used by tests only
@@ -658,20 +644,18 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
/**
* Adds an update listener to {@param animator} that adds springs to the animation.
*/
public void addSpringFromFlingUpdateListener(ValueAnimator animator, float velocity) {
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
boolean shouldSpring = true;
public void addSpringFromFlingUpdateListener(ValueAnimator animator,
float velocity /* release velocity */,
float progress /* portion of the distance to travel*/) {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (shouldSpring
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
absorbSwipeUpVelocity(Math.max(100, Math.abs(
Math.round(velocity * FLING_VELOCITY_MULTIPLIER))));
// calculate the velocity of using the not user controlled interpolator
// of when the container reach the end.
shouldSpring = false;
}
public void onAnimationStart(Animator animator) {
float distance = (float) ((1 - progress) * getHeight()); // px
float settleVelocity = Math.min(0, distance
/ (AllAppsTransitionController.INTERP_COEFF * animator.getDuration())
+ velocity);
absorbSwipeUpVelocity(Math.max(1000, Math.abs(
Math.round(settleVelocity * FLING_VELOCITY_MULTIPLIER))));
}
});
}
@@ -704,7 +688,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mHeaderPaint.setColor(mHeaderColor);
mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
canvas.drawRect(0, 0, getWidth(), mSearchContainer.getTop() + getTranslationY(),
int bottom = mUsingTabs && mHeader.mHeaderCollapsed ? mHeader.getVisibleBottomBound()
: mSearchContainer.getBottom();
canvas.drawRect(0, 0, getWidth(), bottom + getTranslationY(),
mHeaderPaint);
}
}
@@ -781,13 +767,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mAH[AdapterHolder.MAIN].recyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs
&& verticalFadingEdge);
}
private View getOverlayView() {
if (mOverlay == null) {
mOverlay = mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null);
}
return mOverlay;
}
}
@@ -447,4 +447,14 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
public boolean hasOverlappingRendering() {
return false;
}
/**
* Returns distance between left and right app icons
*/
public int getTabWidth() {
DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile();
int totalWidth = (grid.availableWidthPx - getPaddingLeft() - getPaddingRight());
int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
return totalWidth - iconPadding;
}
}
@@ -57,6 +57,8 @@ import com.android.launcher3.views.ScrimView;
*/
public class AllAppsTransitionController
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
@@ -72,8 +72,13 @@ public class FloatingHeaderView extends LinearLayout implements
}
int current = -mCurrentRV.getCurrentScrollY();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
if (headerCollapsed != mHeaderCollapsed) {
AllAppsContainerView parent = (AllAppsContainerView) getParent();
parent.invalidateHeader();
}
}
};
@@ -219,6 +224,8 @@ public class FloatingHeaderView extends LinearLayout implements
mTabsHidden = tabsHidden;
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
mTabLayout.getLayoutParams().width =
mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView.getTabWidth();
mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
mParent = (ViewGroup) mMainRV.getParent();
@@ -429,6 +436,13 @@ public class FloatingHeaderView extends LinearLayout implements
}
return null;
}
/**
* Returns visible height of FloatingHeaderView contents
*/
public int getVisibleBottomBound() {
return getBottom() + mTranslationY;
}
}
@@ -21,7 +21,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.FrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -29,7 +29,7 @@ import com.android.launcher3.R;
/**
* Work profile toggle switch shown at the bottom of AllApps work tab
*/
public class WorkEduCard extends LinearLayout implements View.OnClickListener,
public class WorkEduCard extends FrameLayout implements View.OnClickListener,
Animation.AnimationListener {
private final Launcher mLauncher;
@@ -52,11 +52,24 @@ public class WorkEduCard extends LinearLayout implements View.OnClickListener,
mDismissAnim.setAnimationListener(this);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mDismissAnim.reset();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mDismissAnim.cancel();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
findViewById(R.id.action_btn).setOnClickListener(this);
MarginLayoutParams lp = ((MarginLayoutParams) findViewById(R.id.wrapper).getLayoutParams());
lp.width = mLauncher.getAppsView().getActiveRecyclerView().getTabWidth();
}
@Override
@@ -31,7 +31,6 @@ import android.widget.Button;
import androidx.annotation.RequiresApi;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
@@ -79,7 +78,9 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
clearAnimation();
if (workTabVisible) {
setEnabled(true);
setVisibility(VISIBLE);
if (mWorkEnabled) {
setVisibility(VISIBLE);
}
setAlpha(0);
animate().alpha(1).start();
} else {
@@ -91,7 +92,7 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
public void onClick(View view) {
if (Utilities.ATLEAST_P) {
setEnabled(false);
UI_HELPER_EXECUTOR.post(() -> setToState(!mWorkEnabled));
UI_HELPER_EXECUTOR.post(() -> setWorkProfileEnabled(getContext(), false));
}
}
@@ -101,20 +102,18 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi
public void updateCurrentState(boolean active) {
mWorkEnabled = active;
setEnabled(true);
setCompoundDrawablesRelativeWithIntrinsicBounds(
active ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
setText(active ? R.string.work_apps_pause_btn_text : R.string.work_apps_enable_btn_text);
setVisibility(active ? VISIBLE : GONE);
}
@RequiresApi(Build.VERSION_CODES.P)
protected Boolean setToState(boolean toState) {
UserManager userManager = getContext().getSystemService(UserManager.class);
public static Boolean setWorkProfileEnabled(Context context, boolean enabled) {
UserManager userManager = context.getSystemService(UserManager.class);
boolean showConfirm = false;
for (UserHandle userProfile : UserCache.INSTANCE.get(getContext()).getUserProfiles()) {
for (UserHandle userProfile : UserCache.INSTANCE.get(context).getUserProfiles()) {
if (Process.myUserHandle().equals(userProfile)) {
continue;
}
showConfirm |= !userManager.requestQuietModeEnabled(!toState, userProfile);
showConfirm |= !userManager.requestQuietModeEnabled(!enabled, userProfile);
}
return showConfirm;
}
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 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.launcher3.allapps;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
/**
* Work profile toggle switch shown at the bottom of AllApps work tab
*/
public class WorkPausedCard extends LinearLayout implements View.OnClickListener {
private final Launcher mLauncher;
private Button mBtn;
public WorkPausedCard(Context context) {
this(context, null, 0);
}
public WorkPausedCard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WorkPausedCard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(getContext());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBtn = findViewById(R.id.enable_work_apps);
mBtn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (Utilities.ATLEAST_P) {
setEnabled(false);
UI_HELPER_EXECUTOR.post(() -> WorkModeSwitch.setWorkProfileEnabled(getContext(), true));
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int orientation = getResources().getConfiguration().orientation;
getLayoutParams().height = orientation == Configuration.ORIENTATION_PORTRAIT
? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
super.onLayout(changed, l, t, r, b);
}
}
@@ -473,7 +473,13 @@ public class StatsLogManager implements ResourceBasedOverride {
LAUNCHER_WIDGET_RESIZE_COMPLETED(824),
@UiEvent(doc = "User reconfigured a widget on their home screen.")
LAUNCHER_WIDGET_RECONFIGURED(821)
LAUNCHER_WIDGET_RECONFIGURED(821),
@UiEvent(doc = "User enabled themed icons option in wallpaper & style settings.")
LAUNCHER_THEMED_ICON_ENABLED(836),
@UiEvent(doc = "User disabled themed icons option in wallpaper & style settings.")
LAUNCHER_THEMED_ICON_DISABLED(837)
;
// ADD MORE
@@ -130,8 +130,9 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
packageName);
if (!packageInstaller.verifySessionInfo(sessionInfo)) {
FileLog.d(LOG, "Item info failed session info verification: "
+ workspaceInfo);
FileLog.d(LOG, "Item info failed session info verification. "
+ "Skipping : " + workspaceInfo);
continue;
}
List<LauncherActivityInfo> activities = launcherApps
@@ -217,7 +217,8 @@ public class InstallSessionHelper {
void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
&& verifySessionInfo(sessionInfo)) {
&& verifySessionInfo(sessionInfo)
&& !promiseIconAddedForId(sessionInfo.getSessionId())) {
FileLog.d(LOG, "Adding package name to install queue: "
+ sessionInfo.getAppPackageName());
@@ -234,25 +235,28 @@ public class InstallSessionHelper {
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
&& !promiseIconAddedForId(sessionInfo.getSessionId())
&& !new PackageManagerHelper(mAppContext).isAppInstalled(
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
if (sessionInfo != null) {
Bitmap appIcon = sessionInfo.getAppIcon();
FileLog.d(LOG, String.format(
"Verifying session info. Valid: %b, Session verified: %b, Install reason valid:"
+ " %b, App icon: %s, App label: %s, Promise icon added: %b, "
+ "App installed: %b.",
validSessionInfo,
verify(sessionInfo) != null,
sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER,
appIcon == null ? "null" : IOUtils.toBase64String(appIcon),
sessionInfo.getAppLabel(),
promiseIconAddedForId(sessionInfo.getSessionId()),
new PackageManagerHelper(mAppContext).isAppInstalled(
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo))));
if (Utilities.IS_DEBUG_DEVICE) {
FileLog.d(LOG, String.format(
"Verifying session info. Valid: %b,"
+ " Session verified: %b,"
+ " Install reason valid: %b,"
+ " App icon: %s,"
+ " App label: %s,"
+ " App installed: %b.",
validSessionInfo,
verify(sessionInfo) != null,
sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER,
appIcon == null ? "null" : IOUtils.toBase64String(appIcon),
sessionInfo.getAppLabel(),
new PackageManagerHelper(mAppContext).isAppInstalled(
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo))));
}
} else {
FileLog.d(LOG, "Verifying session info failed: session info null.");
}
@@ -96,6 +96,8 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
private int mNumNotifications;
private ViewGroup mNotificationContainer;
private ViewGroup mWidgetContainer;
private ViewGroup mDeepShortcutContainer;
private ViewGroup mSystemShortcutContainer;
@@ -234,7 +236,7 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
@Override
protected List<View> getChildrenForColorExtraction() {
return Arrays.asList(mSystemShortcutContainer, mDeepShortcutContainer,
return Arrays.asList(mSystemShortcutContainer, mWidgetContainer, mDeepShortcutContainer,
mNotificationContainer);
}
@@ -298,10 +300,24 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
updateHiddenShortcuts();
if (!systemShortcuts.isEmpty()) {
mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
for (SystemShortcut shortcut : systemShortcuts) {
initializeSystemShortcut(
R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
if (shortcut instanceof SystemShortcut.Widgets) {
if (mWidgetContainer == null) {
mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
this);
}
initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
shortcut);
}
}
mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
for (SystemShortcut shortcut : systemShortcuts) {
if (!(shortcut instanceof SystemShortcut.Widgets)) {
initializeSystemShortcut(
R.layout.system_shortcut_icon_only, mSystemShortcutContainer,
shortcut);
}
}
}
} else {
@@ -524,25 +540,34 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
mLauncher.getPopupDataProvider().setChangeListener(null);
}
private View getWidgetsView(ViewGroup container) {
for (int i = container.getChildCount() - 1; i >= 0; --i) {
View systemShortcutView = container.getChildAt(i);
if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
return systemShortcutView;
}
}
return null;
}
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
widgetsView = systemShortcutView;
break;
}
View widgetsView = getWidgetsView(PopupContainerWithArrow.this);
if (widgetsView == null && mWidgetContainer != null) {
widgetsView = getWidgetsView(mWidgetContainer);
}
if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
initializeSystemShortcut(R.layout.system_shortcut_icon_only,
mSystemShortcutContainer, widgetInfo);
if (mWidgetContainer == null) {
mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
PopupContainerWithArrow.this);
}
initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
widgetInfo);
} else {
// If using the expanded system shortcut (as opposed to just the icon), we need
// to reopen the container to ensure measurements etc. all work out. While this
@@ -554,8 +579,10 @@ public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
}
} else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
mSystemShortcutContainer.removeView(widgetsView);
if (mSystemShortcutContainer
!= PopupContainerWithArrow.this
&& mWidgetContainer != null) {
mWidgetContainer.removeView(widgetsView);
} else {
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
@@ -335,12 +335,10 @@ public abstract class AbstractStateChangeTouchController
mCurrentAnimation.dispatchOnStart();
if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
if (mAllAppsOvershootStarted) {
mLauncher.getAppsView().onRelease();
mAllAppsOvershootStarted = false;
} else {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity, progress);
}
}
anim.start();
@@ -103,18 +103,25 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
mLauncher = Launcher.getLauncher(context);
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
layoutInflater, iconClickListener, iconLongClickListener,
widgetPreviewLoader, /* listAdapter= */ this);
widgetPreviewLoader, listDrawableFactory, /* listAdapter= */ this);
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this));
layoutInflater,
/* onHeaderClickListener= */ this,
listDrawableFactory,
/* listAdapter= */ this));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SEARCH_HEADER,
new WidgetsListSearchHeaderViewHolderBinder(
layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this));
layoutInflater,
/* onHeaderClickListener= */ this,
listDrawableFactory,
/* listAdapter= */ this));
}
@Override
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2021 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.launcher3.widget.picker;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST_EXPANDED;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE_EXPANDED;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.SINGLE;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
/** Factory for creating drawables to use as background for list elements. */
final class WidgetsListDrawableFactory {
private final float mTopBottomCornerRadius;
private final float mMiddleCornerRadius;
private final ColorStateList mSurfaceColor;
private final ColorStateList mRippleColor;
WidgetsListDrawableFactory(Context context) {
Resources res = context.getResources();
mTopBottomCornerRadius = res.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
mMiddleCornerRadius = res.getDimension(R.dimen.widget_list_content_corner_radius);
mSurfaceColor = context.getColorStateList(R.color.surface);
mRippleColor = ColorStateList.valueOf(
Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
}
/**
* Creates a drawable for widget header list items. This drawable supports all positions
* in {@link WidgetsListDrawableState}.
*/
Drawable createHeaderBackgroundDrawable() {
StateListDrawable stateList = new StateListDrawable();
stateList.addState(
SINGLE.mStateSet,
createRoundedRectDrawable(mTopBottomCornerRadius, mTopBottomCornerRadius));
stateList.addState(
FIRST_EXPANDED.mStateSet,
createRoundedRectDrawable(mTopBottomCornerRadius, 0));
stateList.addState(
FIRST.mStateSet,
createRoundedRectDrawable(mTopBottomCornerRadius, mMiddleCornerRadius));
stateList.addState(
MIDDLE_EXPANDED.mStateSet,
createRoundedRectDrawable(mMiddleCornerRadius, 0));
stateList.addState(
MIDDLE.mStateSet,
createRoundedRectDrawable(mMiddleCornerRadius, mMiddleCornerRadius));
stateList.addState(
LAST.mStateSet,
createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius));
return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
}
/**
* Creates a drawable for widget content list items. This state list supports the middle and
* last states.
*/
Drawable createContentBackgroundDrawable() {
StateListDrawable stateList = new StateListDrawable();
stateList.addState(
MIDDLE.mStateSet,
createRoundedRectDrawable(0, mMiddleCornerRadius));
stateList.addState(
LAST.mStateSet,
createRoundedRectDrawable(0, mTopBottomCornerRadius));
return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
}
/** Creates a rounded-rect drawable with the specified radii. */
private Drawable createRoundedRectDrawable(float topRadius, float bottomRadius) {
GradientDrawable backgroundMask = new GradientDrawable();
backgroundMask.setColor(mSurfaceColor);
backgroundMask.setShape(GradientDrawable.RECTANGLE);
backgroundMask.setCornerRadii(
new float[]{
topRadius,
topRadius,
topRadius,
topRadius,
bottomRadius,
bottomRadius,
bottomRadius,
bottomRadius
});
return backgroundMask;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 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.launcher3.widget.picker;
/**
* Different possible list position states for an item in the widgets list to have. Note that only
* headers use the expanded state.
*/
enum WidgetsListDrawableState {
FIRST(new int[]{android.R.attr.state_first}),
FIRST_EXPANDED(new int[]{android.R.attr.state_first, android.R.attr.state_expanded}),
MIDDLE(new int[]{android.R.attr.state_middle}),
MIDDLE_EXPANDED(new int[]{android.R.attr.state_middle, android.R.attr.state_expanded}),
LAST(new int[]{android.R.attr.state_last}),
SINGLE(new int[]{android.R.attr.state_single});
final int[] mStateSet;
WidgetsListDrawableState(int[] stateSet) {
mStateSet = stateSet;
}
static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast, boolean isExpanded) {
if (isFirst && isLast) return SINGLE;
if (isFirst && isExpanded) return FIRST_EXPANDED;
if (isFirst) return FIRST;
if (isLast) return LAST;
if (isExpanded) return MIDDLE_EXPANDED;
return MIDDLE;
}
}
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2021 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.launcher3.widget.picker;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
/** Helper class for creating drawables to use as background for list elements. */
final class WidgetsListDrawables {
private WidgetsListDrawables() {}
/** Creates a list background drawable with the specified radii. */
static Drawable createListBackgroundDrawable(
Context context,
float topRadius,
float bottomRadius) {
GradientDrawable backgroundMask = new GradientDrawable();
backgroundMask.setColor(context.getColorStateList(R.color.surface));
backgroundMask.setShape(GradientDrawable.RECTANGLE);
backgroundMask.setCornerRadii(
new float[]{
topRadius,
topRadius,
topRadius,
topRadius,
bottomRadius,
bottomRadius,
bottomRadius,
bottomRadius
});
return new RippleDrawable(
/* color= */ ColorStateList.valueOf(
Themes.getAttrColor(context, android.R.attr.colorControlHighlight)),
/* content= */ backgroundMask,
/* mask= */ backgroundMask);
}
}
@@ -60,9 +60,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
@Nullable private Drawable mIconDrawable;
private final int mIconSize;
private final int mBottomMarginSize;
private final float mTopBottomCornerRadius;
private final float mMiddleCornerRadius;
private final float mJoinedCornerRadius;
private ImageView mAppIcon;
private TextView mTitle;
@@ -70,6 +67,7 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
private CheckBox mExpandToggle;
private boolean mIsExpanded = false;
@Nullable private WidgetsListDrawableState mListDrawableState;
public WidgetsListHeader(Context context) {
this(context, /* attrs= */ null);
@@ -90,12 +88,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
grid.iconSizePx);
mBottomMarginSize =
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
mTopBottomCornerRadius =
getResources().getDimension(R.dimen.widget_list_top_bottom_corner_radius);
mMiddleCornerRadius =
getResources().getDimension(R.dimen.widget_list_content_corner_radius);
mJoinedCornerRadius =
getResources().getDimension(R.dimen.widget_list_content_joined_corner_radius);
}
@Override
@@ -163,6 +155,14 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
}
}
/** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
@UiThread
public void setListDrawableState(WidgetsListDrawableState state) {
if (state == mListDrawableState) return;
this.mListDrawableState = state;
refreshDrawableState();
}
/** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
@UiThread
public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
@@ -263,20 +263,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
verifyHighRes();
}
/** Updates the list to have a background drawable with the appropriate corner radii. */
@UiThread
public void updateListBackground(boolean isFirst, boolean isLast, boolean isExpanded) {
float topRadius = isFirst ? mTopBottomCornerRadius : mMiddleCornerRadius;
float bottomRadius = isLast
? mTopBottomCornerRadius
: isExpanded
? mJoinedCornerRadius
: mMiddleCornerRadius;
setBackground(
WidgetsListDrawables.createListBackgroundDrawable(
getContext(), topRadius, bottomRadius));
}
private void setTitles(WidgetsListSearchHeaderEntry entry) {
mTitle.setText(entry.mPkgItem.title);
@@ -300,6 +286,17 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
}
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
// Augment the state set from the super implementation with the custom states from
// mListDrawableState.
int[] drawableState =
super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
return drawableState;
}
/** Verifies that the current icon is high-res otherwise posts a request to load the icon. */
public void verifyHighRes() {
if (mIconLoadRequest != null) {
@@ -30,13 +30,16 @@ public final class WidgetsListHeaderViewHolderBinder implements
ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
OnHeaderClickListener onHeaderClickListener,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter;
}
@@ -44,7 +47,7 @@ public final class WidgetsListHeaderViewHolderBinder implements
public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
R.layout.widgets_list_row_header, parent, false);
header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
return new WidgetsListHeaderHolder(header);
}
@@ -52,12 +55,13 @@ public final class WidgetsListHeaderViewHolderBinder implements
public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.updateListBackground(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown());
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(
isExpanded,
@@ -31,13 +31,16 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
OnHeaderClickListener onHeaderClickListener,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter;
}
@@ -45,7 +48,7 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) {
WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
R.layout.widgets_list_row_header, parent, false);
header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
return new WidgetsListSearchHeaderHolder(header);
}
@@ -53,12 +56,13 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
WidgetsListSearchHeaderEntry data, int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.updateListBackground(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown());
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(isExpanded,
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2021 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.launcher3.widget.picker;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TableLayout;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
/**
* Extension of {@link TableLayout} to support the drawable states used by
* {@link WidgetsListDrawableState}.
*/
public class WidgetsListTableView extends TableLayout {
@Nullable private WidgetsListDrawableState mListDrawableState;
public WidgetsListTableView(Context context) {
super(context);
}
public WidgetsListTableView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
@UiThread
public void setListDrawableState(WidgetsListDrawableState state) {
if (state == mListDrawableState) return;
mListDrawableState = state;
refreshDrawableState();
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
// Augment the state set from the super implementation with the custom states from
// mListDrawableState.
int[] drawableState =
super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
return drawableState;
}
}
@@ -15,8 +15,10 @@
*/
package com.android.launcher3.widget.picker;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -51,10 +53,8 @@ public final class WidgetsListTableViewHolderBinder
private final OnClickListener mIconClickListener;
private final OnLongClickListener mIconLongClickListener;
private final WidgetPreviewLoader mWidgetPreviewLoader;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter;
private final float mTopBottomCornerRadius;
private final float mMiddleCornerRadius;
private final float mJoinedCornerRadius;
private boolean mApplyBitmapDeferred = false;
public WidgetsListTableViewHolderBinder(
@@ -63,19 +63,14 @@ public final class WidgetsListTableViewHolderBinder
OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener,
WidgetPreviewLoader widgetPreviewLoader,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
mWidgetPreviewLoader = widgetPreviewLoader;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter;
Resources resources = context.getResources();
mTopBottomCornerRadius =
resources.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
mMiddleCornerRadius =
resources.getDimension(R.dimen.widget_list_content_corner_radius);
mJoinedCornerRadius =
resources.getDimension(R.dimen.widget_list_content_joined_corner_radius);
}
/**
@@ -97,28 +92,25 @@ public final class WidgetsListTableViewHolderBinder
Log.v(TAG, "\nonCreateViewHolder");
}
ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
R.layout.widgets_table_container, parent, false);
return new WidgetsRowViewHolder(container);
WidgetsRowViewHolder viewHolder =
new WidgetsRowViewHolder(mLayoutInflater.inflate(
R.layout.widgets_table_container, parent, false));
viewHolder.mTableContainer.setBackgroundDrawable(
mListDrawableFactory.createContentBackgroundDrawable());
return viewHolder;
}
@Override
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
int position) {
TableLayout table = holder.mTableContainer;
WidgetsListTableView table = holder.mTableContainer;
if (DEBUG) {
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
entry.mWidgets.size(), table.getChildCount()));
}
// The content is always joined to an expanded header above.
float topRadius = mJoinedCornerRadius;
float bottomRadius = position == mWidgetsListAdapter.getItemCount() - 1
? mTopBottomCornerRadius
: mMiddleCornerRadius;
table.setBackgroundDrawable(
WidgetsListDrawables.createListBackgroundDrawable(
holder.itemView.getContext(), topRadius, bottomRadius));
table.setListDrawableState(
position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE);
List<ArrayList<WidgetItem>> widgetItemsTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
@@ -15,8 +15,7 @@
*/
package com.android.launcher3.widget.picker;
import android.view.ViewGroup;
import android.widget.TableLayout;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -25,9 +24,9 @@ import com.android.launcher3.R;
/** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
public final class WidgetsRowViewHolder extends ViewHolder {
public final TableLayout mTableContainer;
public final WidgetsListTableView mTableContainer;
public WidgetsRowViewHolder(ViewGroup v) {
public WidgetsRowViewHolder(View v) {
super(v);
mTableContainer = v.findViewById(R.id.widgets_table);
@@ -293,7 +293,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
try {
final AppIconMenu menu = allApps.
getAppIcon(APP_NAME).
openMenu();
openDeepShortcutMenu();
executeOnLauncher(
launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
@@ -341,7 +341,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
try {
final AppIconMenu menu = allApps
.getAppIcon(APP_NAME)
.openMenu();
.openDeepShortcutMenu();
final AppIconMenuItem menuItem0 = menu.getMenuItem(0);
final AppIconMenuItem menuItem2 = menu.getMenuItem(2);
@@ -51,6 +51,16 @@ public final class AppIcon extends Launchable {
}
}
/**
* Long-clicks the icon to open its menu, and looks at the deep shortcuts container only.
*/
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
}
}
@Override
protected void addExpectedEventsForLongClick() {
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);