diff --git a/Android.bp b/Android.bp index d04dca0dfc..8b7eb54b9e 100644 --- a/Android.bp +++ b/Android.bp @@ -278,16 +278,15 @@ android_library { srcs: [ ":launcher-src-no-build-config", ], - resource_dirs: [ - "quickstep/res", - ], + resource_dirs: [], libs: [ "framework-statsd", ], static_libs: [ + "QuickstepResLib", "SystemUI-statsd", "SystemUISharedLib", - "Launcher3CommonDepsLib" + "Launcher3CommonDepsLib", ], manifest: "quickstep/AndroidManifest.xml", platform_apis: true, diff --git a/quickstep/res/drawable/task_menu_item_bg.xml b/quickstep/res/drawable/task_menu_item_bg.xml index b6a8b909ee..16c13ebebc 100644 --- a/quickstep/res/drawable/task_menu_item_bg.xml +++ b/quickstep/res/drawable/task_menu_item_bg.xml @@ -15,7 +15,8 @@ limitations under the License. --> - - - + + + diff --git a/quickstep/res/layout/task_menu_with_arrow.xml b/quickstep/res/layout/task_menu_with_arrow.xml new file mode 100644 index 0000000000..38573fd1e5 --- /dev/null +++ b/quickstep/res/layout/task_menu_with_arrow.xml @@ -0,0 +1,33 @@ + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/task_view_menu_option.xml b/quickstep/res/layout/task_view_menu_option.xml index 5978b97dd4..8a8fc36b84 100644 --- a/quickstep/res/layout/task_view_menu_option.xml +++ b/quickstep/res/layout/task_view_menu_option.xml @@ -18,7 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="vertical" + android:orientation="horizontal" android:paddingTop="@dimen/task_card_menu_option_vertical_padding" android:paddingBottom="@dimen/task_card_menu_option_vertical_padding" android:background="@drawable/task_menu_item_bg" diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 0b534e31e9..e48c9e8b6b 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -27,7 +27,7 @@ 22dp 4dp 2dp - 200dp + 234dp 48dp 16dp @@ -91,7 +91,7 @@ 8dp 8dp 3dp - 12dp + 16dp 10dp diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java new file mode 100644 index 0000000000..57dad08456 --- /dev/null +++ b/quickstep/src/com/android/quickstep/KtR.java @@ -0,0 +1,39 @@ +/* + * 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.quickstep; + +import com.android.launcher3.R; + +/** + * Bridge class to allow using resources in Kotlin. + *
+ * TODO(b/204069723) Can't use resources directly in Kotlin + */ +public class KtR { + public static final class id { + public static int menu_option_layout = R.id.menu_option_layout; + } + + public static final class dimen { + public static int task_menu_spacing = R.dimen.task_menu_spacing; + } + + public static final class layout { + public static int task_menu_with_arrow = R.layout.task_menu_with_arrow; + public static int task_view_menu_option = R.layout.task_view_menu_option; + } +} diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index ae81573684..eace227c5c 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -119,7 +119,11 @@ public class GroupedTaskView extends TaskView { } protected boolean showTaskMenuWithContainer(IconView iconView) { - return TaskMenuView.showForTask(mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1]); + if (mActivity.getDeviceProfile().overviewShowAsGrid) { + return TaskMenuViewWithArrow.Companion.showForTask(mTaskIdAttributeContainer[0]); + } else { + return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]); + } } public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) { diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index 77ac373827..d55b89c5a1 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -34,7 +34,6 @@ import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver.OnScrollChangedListener; import android.widget.LinearLayout; @@ -270,15 +269,9 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams(); int padding = getResources() .getDimensionPixelSize(R.dimen.task_menu_vertical_padding); - if (deviceProfile.overviewShowAsGrid) { - // TODO(b/193432925) temporary so it doesn't look terrible on large screen - params.width = - getContext().getResources().getDimensionPixelSize(R.dimen.task_menu_width_grid); - } else { - params.width = orientationHandler - .getTaskMenuWidth(taskContainer.getThumbnailView(), - deviceProfile) - (2 * padding); - } + params.width = orientationHandler + .getTaskMenuWidth(taskContainer.getThumbnailView(), + deviceProfile) - (2 * padding); // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start params.gravity = Gravity.LEFT; setLayoutParams(params); @@ -349,13 +342,4 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect); } - public View findMenuItemByText(String text) { - for (int i = mOptionLayout.getChildCount() - 1; i >= 0; --i) { - final ViewGroup menuOptionView = (ViewGroup) mOptionLayout.getChildAt(i); - if (text.equals(menuOptionView.findViewById(R.id.text).getText())) { - return menuOptionView; - } - } - return null; - } } diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt index 9b86c73170..5059f8b532 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt +++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt @@ -1,14 +1,167 @@ +/* + * 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.quickstep.views -import android.util.Log +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.content.Context +import android.graphics.Rect +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.RectShape +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.android.launcher3.BaseDraggingActivity +import com.android.launcher3.DeviceProfile +import com.android.launcher3.R +import com.android.launcher3.popup.ArrowPopup +import com.android.launcher3.popup.SystemShortcut +import com.android.launcher3.util.Themes +import com.android.quickstep.KtR +import com.android.quickstep.TaskOverlayFactory +import com.android.quickstep.views.TaskView.TaskIdAttributeContainer -// TODO(http://b/193432925) -class TaskMenuViewWithArrow { +class TaskMenuViewWithArrow : ArrowPopup { companion object { const val TAG = "TaskMenuViewWithArrow" - fun logSomething() { - Log.d(TAG, "It worked!") + fun showForTask(taskContainer: TaskIdAttributeContainer): Boolean { + val activity = BaseDraggingActivity + .fromContext(taskContainer.taskView.context) + val taskMenuViewWithArrow = activity.layoutInflater + .inflate(KtR.layout.task_menu_with_arrow, activity.dragLayer, false) as TaskMenuViewWithArrow<*> + + return taskMenuViewWithArrow.populateAndShowForTask(taskContainer) } } + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + init { + clipToOutline = true + } + + private val menuWidth = context.resources.getDimensionPixelSize(R.dimen.task_menu_width_grid) + + private lateinit var taskView: TaskView + private lateinit var optionLayout: LinearLayout + private lateinit var taskContainer: TaskIdAttributeContainer + + override fun isOfType(type: Int): Boolean = type and TYPE_TASK_MENU != 0 + + override fun getTargetObjectLocation(outPos: Rect?) { + popupContainer.getDescendantRectRelativeToSelf(taskView.iconView, outPos) + } + + override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean { + if (ev?.action == MotionEvent.ACTION_DOWN) { + if (!popupContainer.isEventOverView(this, ev)) { + close(true) + return true + } + } + return false + } + + override fun onFinishInflate() { + super.onFinishInflate() + optionLayout = findViewById(KtR.id.menu_option_layout) + } + + private fun populateAndShowForTask(taskContainer: TaskIdAttributeContainer): Boolean { + if (isAttachedToWindow) { + return false + } + + taskView = taskContainer.taskView + this.taskContainer = taskContainer + if (!populateMenu()) return false + show() + return true + } + + /** @return true if successfully able to populate task view menu, false otherwise + */ + private fun populateMenu(): Boolean { + // Icon may not be loaded + if (taskContainer.task.icon == null) return false + + addMenuOptions() + return true + } + + private fun addMenuOptions() { + // Add the options + TaskOverlayFactory + .getEnabledShortcuts(taskView, mActivityContext.deviceProfile, taskContainer) + .forEach { this.addMenuOption(it) } + + // Add the spaces between items + val divider = ShapeDrawable(RectShape()) + divider.paint.color = resources.getColor(android.R.color.transparent) + val dividerSpacing = resources.getDimension(KtR.dimen.task_menu_spacing).toInt() + optionLayout.showDividers = SHOW_DIVIDER_MIDDLE + + // Set the orientation, which makes the menu show + val recentsView: RecentsView<*, *> = mActivityContext.getOverviewPanel() + val orientationHandler = recentsView.pagedOrientationHandler + val deviceProfile: DeviceProfile = mActivityContext.deviceProfile + orientationHandler.setTaskOptionsMenuLayoutOrientation( + deviceProfile, + optionLayout, + dividerSpacing, + divider + ) + } + + private fun addMenuOption(menuOption: SystemShortcut<*>) { + val menuOptionView = mActivityContext.layoutInflater.inflate( + KtR.layout.task_view_menu_option, this, false + ) as LinearLayout + menuOption.setIconAndLabelFor( + menuOptionView.findViewById(R.id.icon), + menuOptionView.findViewById(R.id.text) + ) + val lp = menuOptionView.layoutParams as LayoutParams + lp.width = menuWidth + menuOptionView.setOnClickListener { view: View? -> menuOption.onClick(view) } + optionLayout.addView(menuOptionView) + } + + override fun assignMarginsAndBackgrounds(viewGroup: ViewGroup) { + assignMarginsAndBackgrounds(this, Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)) + } + + override fun onCreateOpenAnimation(anim: AnimatorSet) { + anim.play( + ObjectAnimator.ofFloat( + taskContainer.thumbnailView, TaskThumbnailView.DIM_ALPHA, + TaskView.MAX_PAGE_SCRIM_ALPHA + ) + ) + } + + override fun onCreateCloseAnimation(anim: AnimatorSet) { + anim.play( + ObjectAnimator.ofFloat(taskContainer.thumbnailView, TaskThumbnailView.DIM_ALPHA, 0f) + ) + } } \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 2be3160c9f..89961274b3 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -95,7 +95,6 @@ import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.TaskCornerRadius; -import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper; import com.android.systemui.shared.recents.model.Task; @@ -838,9 +837,11 @@ public class TaskView extends FrameLayout implements Reusable { } protected boolean showTaskMenuWithContainer(IconView iconView) { - // TODO(http://b/193432925) - if (DEBUG) TaskMenuViewWithArrow.Companion.logSomething(); - return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]); + if (mActivity.getDeviceProfile().overviewShowAsGrid) { + return TaskMenuViewWithArrow.Companion.showForTask(mTaskIdAttributeContainer[0]); + } else { + return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]); + } } protected void setIcon(IconView iconView, Drawable icon) { diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index 223091485c..5a1e4bf27e 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -234,7 +234,7 @@ public abstract class ArrowPopup * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColorIds}. * Otherwise, we will use this color for all child views. */ - private void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) { + protected void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) { int[] colors = null; if (backgroundColor == Color.TRANSPARENT) { // Lazily get the colors so they match the current wallpaper colors. @@ -445,7 +445,7 @@ public abstract class ArrowPopup animateOpen(); } - private void setupForDisplay() { + protected void setupForDisplay() { setVisibility(View.INVISIBLE); mIsOpen = true; getPopupContainer().addView(this); @@ -482,7 +482,7 @@ public abstract class ArrowPopup mArrow.setVisibility(show && shouldAddArrow() ? VISIBLE : INVISIBLE); } - private void addArrow() { + protected void addArrow() { getPopupContainer().addView(mArrow); mArrow.setX(getX() + getArrowLeft()); @@ -686,12 +686,13 @@ public abstract class ArrowPopup return getChildCount() > 0 ? getChildAt(0) : this; } - private void animateOpen() { + protected void animateOpen() { setVisibility(View.VISIBLE); mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY, OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION, DECELERATED_EASE); + onCreateOpenAnimation(mOpenCloseAnimator); mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -784,6 +785,11 @@ public abstract class ArrowPopup mOpenCloseAnimator.start(); } + /** + * Called when creating the open transition allowing subclass can add additional animations. + */ + protected void onCreateOpenAnimation(AnimatorSet anim) { } + /** * Called when creating the close transition allowing subclass can add additional animations. */