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 mAppsView.setOnIconLongClickListener(this::onIconLongClicked); 83 mActivity.getSecondaryDisplayPredictions() 84 .setLongClickListener(mAppsView, this::onIconLongClicked); 85 // Setup workspace 86 mWorkspace = findViewById(R.id.workspace_grid); 87 mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(), 88 this::onIconLongClicked); 89 mWorkspace.setAdapter(mPinnedAppsAdapter); 90 mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns); 91 } 92 93 /** 94 * {@inheritDoc} 95 */ 96 @Override onAttachedToWindow()97 protected void onAttachedToWindow() { 98 super.onAttachedToWindow(); 99 mPinnedAppsAdapter.init(); 100 } 101 102 /** 103 * {@inheritDoc} 104 */ 105 @Override onDetachedFromWindow()106 protected void onDetachedFromWindow() { 107 super.onDetachedFromWindow(); 108 mPinnedAppsAdapter.destroy(); 109 } 110 111 /** 112 * {@inheritDoc} 113 */ 114 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)115 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 116 int width = MeasureSpec.getSize(widthMeasureSpec); 117 int height = MeasureSpec.getSize(heightMeasureSpec); 118 setMeasuredDimension(width, height); 119 120 DeviceProfile grid = mActivity.getDeviceProfile(); 121 int count = getChildCount(); 122 for (int i = 0; i < count; i++) { 123 final View child = getChildAt(i); 124 if (child == mAppsView) { 125 int horizontalPadding = (2 * grid.desiredWorkspaceHorizontalMarginPx) 126 + grid.cellLayoutPaddingPx.left + grid.cellLayoutPaddingPx.right; 127 int verticalPadding = 128 grid.cellLayoutPaddingPx.top + grid.cellLayoutPaddingPx.bottom; 129 130 int maxWidth = 131 grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding; 132 int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth); 133 134 int maxHeight = 135 grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding; 136 int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight); 137 138 mAppsView.measure( 139 makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY)); 140 } else if (child == mAllAppsButton) { 141 int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY); 142 mAllAppsButton.measure(appsButtonSpec, appsButtonSpec); 143 } else if (child == mWorkspace) { 144 measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec, 145 grid.iconSizePx + grid.edgeMarginPx); 146 } else { 147 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 148 } 149 } 150 } 151 152 private class CloseAllAppsTouchController implements TouchController { 153 154 @Override onControllerTouchEvent(MotionEvent ev)155 public boolean onControllerTouchEvent(MotionEvent ev) { 156 return false; 157 } 158 159 @Override onControllerInterceptTouchEvent(MotionEvent ev)160 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 161 if (!mActivity.isAppDrawerShown()) { 162 return false; 163 } 164 165 if (AbstractFloatingView.getTopOpenView(mActivity) != null) { 166 return false; 167 } 168 169 if (ev.getAction() == MotionEvent.ACTION_DOWN 170 && !isEventOverView(mActivity.getAppsView(), ev)) { 171 mActivity.showAppDrawer(false); 172 return true; 173 } 174 return false; 175 } 176 } 177 getPinnedAppsAdapter()178 public PinnedAppsAdapter getPinnedAppsAdapter() { 179 return mPinnedAppsAdapter; 180 } 181 onIconLongClicked(View v)182 private boolean onIconLongClicked(View v) { 183 if (!(v instanceof BubbleTextView)) { 184 return false; 185 } 186 if (PopupContainerWithArrow.getOpen(mActivity) != null) { 187 // There is already an items container open, so don't open this one. 188 v.clearFocus(); 189 return false; 190 } 191 ItemInfo item = (ItemInfo) v.getTag(); 192 if (!ShortcutUtil.supportsShortcuts(item)) { 193 return false; 194 } 195 PopupDataProvider popupDataProvider = mActivity.getPopupDataProvider(); 196 if (popupDataProvider == null) { 197 return false; 198 } 199 200 // order of this list will reflect in the popup 201 List<SystemShortcut> systemShortcuts = new ArrayList<>(); 202 systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v)); 203 // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled. 204 if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { 205 systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v)); 206 } 207 int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item); 208 final PopupContainerWithArrow<SecondaryDisplayLauncher> container; 209 if (ENABLE_MATERIAL_U_POPUP.get()) { 210 container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( 211 R.layout.popup_container_material_u, mActivity.getDragLayer(), false); 212 container.populateAndShowRowsMaterialU((BubbleTextView) v, deepShortcutCount, 213 systemShortcuts); 214 } else { 215 container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( 216 R.layout.popup_container, mActivity.getDragLayer(), false); 217 container.populateAndShow( 218 (BubbleTextView) v, 219 deepShortcutCount, 220 Collections.emptyList(), 221 systemShortcuts); 222 } 223 container.requestFocus(); 224 225 if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { 226 return true; 227 } 228 229 DragOptions options = new DragOptions(); 230 DeviceProfile grid = mActivity.getDeviceProfile(); 231 options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; 232 options.preDragCondition = container.createPreDragCondition(false); 233 if (options.preDragCondition == null) { 234 options.preDragCondition = new DragOptions.PreDragCondition() { 235 private DragView<SecondaryDisplayLauncher> mDragView; 236 237 @Override 238 public boolean shouldStartDrag(double distanceDragged) { 239 return mDragView != null && mDragView.isAnimationFinished(); 240 } 241 242 @Override 243 public void onPreDragStart(DropTarget.DragObject dragObject) { 244 mDragView = dragObject.dragView; 245 if (!shouldStartDrag(0)) { 246 mDragView.setOnAnimationEndCallback(() -> { 247 mActivity.beginDragShared(v, mActivity.getAppsView(), options); 248 }); 249 } 250 } 251 252 @Override 253 public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { 254 mDragView = null; 255 } 256 }; 257 } 258 mActivity.beginDragShared(v, mActivity.getAppsView(), options); 259 return true; 260 } 261 } 262