1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.secondarydisplay; 17 18 import static android.view.View.MeasureSpec.EXACTLY; 19 import static android.view.View.MeasureSpec.makeMeasureSpec; 20 21 import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP; 22 import static com.android.launcher3.popup.SystemShortcut.APP_INFO; 23 24 import android.content.Context; 25 import android.util.AttributeSet; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.widget.GridView; 29 30 import com.android.launcher3.AbstractFloatingView; 31 import com.android.launcher3.BubbleTextView; 32 import com.android.launcher3.DeviceProfile; 33 import com.android.launcher3.DropTarget; 34 import com.android.launcher3.R; 35 import com.android.launcher3.allapps.ActivityAllAppsContainerView; 36 import com.android.launcher3.config.FeatureFlags; 37 import com.android.launcher3.dragndrop.DragOptions; 38 import com.android.launcher3.dragndrop.DragView; 39 import com.android.launcher3.model.data.ItemInfo; 40 import com.android.launcher3.popup.PopupContainerWithArrow; 41 import com.android.launcher3.popup.PopupDataProvider; 42 import com.android.launcher3.popup.SystemShortcut; 43 import com.android.launcher3.util.ShortcutUtil; 44 import com.android.launcher3.util.TouchController; 45 import com.android.launcher3.views.BaseDragLayer; 46 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** 52 * DragLayer for Secondary launcher 53 */ 54 public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> { 55 56 private View mAllAppsButton; 57 private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView; 58 59 private GridView mWorkspace; 60 private PinnedAppsAdapter mPinnedAppsAdapter; 61 SecondaryDragLayer(Context context, AttributeSet attrs)62 public SecondaryDragLayer(Context context, AttributeSet attrs) { 63 super(context, attrs, 1 /* alphaChannelCount */); 64 recreateControllers(); 65 } 66 67 @Override recreateControllers()68 public void recreateControllers() { 69 mControllers = new TouchController[]{new CloseAllAppsTouchController(), 70 mActivity.getDragController()}; 71 } 72 73 /** 74 * {@inheritDoc} 75 */ 76 @Override onFinishInflate()77 protected void onFinishInflate() { 78 super.onFinishInflate(); 79 mAllAppsButton = findViewById(R.id.all_apps_button); 80 81 mAppsView = findViewById(R.id.apps_view); 82 // Setup workspace 83 mWorkspace = findViewById(R.id.workspace_grid); 84 mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(), 85 this::onIconLongClicked); 86 mWorkspace.setAdapter(mPinnedAppsAdapter); 87 mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns); 88 } 89 90 /** 91 * {@inheritDoc} 92 */ 93 @Override onAttachedToWindow()94 protected void onAttachedToWindow() { 95 super.onAttachedToWindow(); 96 mPinnedAppsAdapter.init(); 97 } 98 99 /** 100 * {@inheritDoc} 101 */ 102 @Override onDetachedFromWindow()103 protected void onDetachedFromWindow() { 104 super.onDetachedFromWindow(); 105 mPinnedAppsAdapter.destroy(); 106 } 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)112 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 113 int width = MeasureSpec.getSize(widthMeasureSpec); 114 int height = MeasureSpec.getSize(heightMeasureSpec); 115 setMeasuredDimension(width, height); 116 117 DeviceProfile grid = mActivity.getDeviceProfile(); 118 int count = getChildCount(); 119 for (int i = 0; i < count; i++) { 120 final View child = getChildAt(i); 121 if (child == mAppsView) { 122 int horizontalPadding = (2 * grid.desiredWorkspaceHorizontalMarginPx) 123 + grid.cellLayoutPaddingPx.left + grid.cellLayoutPaddingPx.right; 124 int verticalPadding = 125 grid.cellLayoutPaddingPx.top + grid.cellLayoutPaddingPx.bottom; 126 127 int maxWidth = 128 grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding; 129 int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth); 130 131 int maxHeight = 132 grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding; 133 int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight); 134 135 mAppsView.measure( 136 makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY)); 137 } else if (child == mAllAppsButton) { 138 int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY); 139 mAllAppsButton.measure(appsButtonSpec, appsButtonSpec); 140 } else if (child == mWorkspace) { 141 measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec, 142 grid.iconSizePx + grid.edgeMarginPx); 143 } else { 144 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 145 } 146 } 147 } 148 149 private class CloseAllAppsTouchController implements TouchController { 150 151 @Override onControllerTouchEvent(MotionEvent ev)152 public boolean onControllerTouchEvent(MotionEvent ev) { 153 return false; 154 } 155 156 @Override onControllerInterceptTouchEvent(MotionEvent ev)157 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 158 if (!mActivity.isAppDrawerShown()) { 159 return false; 160 } 161 162 if (AbstractFloatingView.getTopOpenView(mActivity) != null) { 163 return false; 164 } 165 166 if (ev.getAction() == MotionEvent.ACTION_DOWN 167 && !isEventOverView(mActivity.getAppsView(), ev)) { 168 mActivity.showAppDrawer(false); 169 return true; 170 } 171 return false; 172 } 173 } 174 getPinnedAppsAdapter()175 public PinnedAppsAdapter getPinnedAppsAdapter() { 176 return mPinnedAppsAdapter; 177 } 178 onIconLongClicked(View v)179 boolean onIconLongClicked(View v) { 180 if (!(v instanceof BubbleTextView)) { 181 return false; 182 } 183 if (PopupContainerWithArrow.getOpen(mActivity) != null) { 184 // There is already an items container open, so don't open this one. 185 v.clearFocus(); 186 return false; 187 } 188 ItemInfo item = (ItemInfo) v.getTag(); 189 if (!ShortcutUtil.supportsShortcuts(item)) { 190 return false; 191 } 192 PopupDataProvider popupDataProvider = mActivity.getPopupDataProvider(); 193 if (popupDataProvider == null) { 194 return false; 195 } 196 197 // order of this list will reflect in the popup 198 List<SystemShortcut> systemShortcuts = new ArrayList<>(); 199 systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v)); 200 // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled. 201 if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { 202 systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v)); 203 } 204 int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item); 205 final PopupContainerWithArrow<SecondaryDisplayLauncher> container; 206 if (ENABLE_MATERIAL_U_POPUP.get()) { 207 container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( 208 R.layout.popup_container_material_u, mActivity.getDragLayer(), false); 209 container.populateAndShowRowsMaterialU((BubbleTextView) v, deepShortcutCount, 210 systemShortcuts); 211 } else { 212 container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( 213 R.layout.popup_container, mActivity.getDragLayer(), false); 214 container.populateAndShow( 215 (BubbleTextView) v, 216 deepShortcutCount, 217 Collections.emptyList(), 218 systemShortcuts); 219 } 220 container.requestFocus(); 221 222 if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { 223 return true; 224 } 225 226 DragOptions options = new DragOptions(); 227 DeviceProfile grid = mActivity.getDeviceProfile(); 228 options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; 229 options.preDragCondition = container.createPreDragCondition(false); 230 if (options.preDragCondition == null) { 231 options.preDragCondition = new DragOptions.PreDragCondition() { 232 private DragView<SecondaryDisplayLauncher> mDragView; 233 234 @Override 235 public boolean shouldStartDrag(double distanceDragged) { 236 return mDragView != null && mDragView.isScaleAnimationFinished(); 237 } 238 239 @Override 240 public void onPreDragStart(DropTarget.DragObject dragObject) { 241 mDragView = dragObject.dragView; 242 if (!shouldStartDrag(0)) { 243 mDragView.setOnScaleAnimEndCallback(() -> 244 mActivity.beginDragShared(v, mActivity.getAppsView(), options)); 245 } 246 } 247 248 @Override 249 public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { 250 mDragView = null; 251 } 252 }; 253 } 254 mActivity.beginDragShared(v, mActivity.getAppsView(), options); 255 return true; 256 } 257 } 258