1 /* 2 * Copyright (C) 2022 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; 17 18 import android.content.Context; 19 import android.graphics.Canvas; 20 import android.graphics.Rect; 21 import android.graphics.drawable.Drawable; 22 import android.util.AttributeSet; 23 import android.view.View; 24 25 import com.android.launcher3.celllayout.CellLayoutLayoutParams; 26 import com.android.launcher3.celllayout.MulticellReorderAlgorithm; 27 import com.android.launcher3.util.CellAndSpan; 28 import com.android.launcher3.util.GridOccupancy; 29 30 /** 31 * CellLayout that simulates a split in the middle for use in foldable devices. 32 */ 33 public class MultipageCellLayout extends CellLayout { 34 35 private final Drawable mLeftBackground; 36 private final Drawable mRightBackground; 37 38 private boolean mSeamWasAdded = false; 39 MultipageCellLayout(Context context)40 public MultipageCellLayout(Context context) { 41 this(context, null); 42 } 43 MultipageCellLayout(Context context, AttributeSet attrs)44 public MultipageCellLayout(Context context, AttributeSet attrs) { 45 this(context, attrs, 0); 46 } 47 48 @Override findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY, int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan)49 protected int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY, 50 int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) { 51 return createReorderAlgorithm().simulateSeam( 52 () -> super.findNearestArea(relativeXPos, relativeYPos, minSpanX, minSpanY, spanX, 53 spanY, ignoreOccupied, result, resultSpan)); 54 } 55 56 @Override getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX, int spanY, View dragView, int[] resultDirection)57 public void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX, 58 int spanY, View dragView, int[] resultDirection) { 59 createReorderAlgorithm().simulateSeam( 60 () -> { 61 super.getDirectionVectorForDrop(dragViewCenterX, dragViewCenterY, spanX, spanY, 62 dragView, resultDirection); 63 return 0; 64 }); 65 } 66 67 @Override isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY, View dragView, int[] result)68 public boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY, 69 View dragView, int[] result) { 70 return createReorderAlgorithm().simulateSeam( 71 () -> super.isNearestDropLocationOccupied(pixelX, pixelY, spanX, spanY, dragView, 72 result)); 73 } 74 MultipageCellLayout(Context context, AttributeSet attrs, int defStyle)75 public MultipageCellLayout(Context context, AttributeSet attrs, int defStyle) { 76 super(context, attrs, defStyle); 77 mLeftBackground = getContext().getDrawable(R.drawable.bg_celllayout); 78 mLeftBackground.setCallback(this); 79 mLeftBackground.setAlpha(0); 80 81 mRightBackground = getContext().getDrawable(R.drawable.bg_celllayout); 82 mRightBackground.setCallback(this); 83 mRightBackground.setAlpha(0); 84 85 DeviceProfile deviceProfile = mActivity.getDeviceProfile(); 86 87 mCountX = deviceProfile.inv.numColumns * 2; 88 mCountY = deviceProfile.inv.numRows; 89 setGridSize(mCountX, mCountY); 90 } 91 92 @Override createAreaForResize(int cellX, int cellY, int spanX, int spanY, View dragView, int[] direction, boolean commit)93 boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, View dragView, 94 int[] direction, boolean commit) { 95 // Add seam to x position 96 if (cellX >= mCountX / 2) { 97 cellX++; 98 } 99 int finalCellX = cellX; 100 return createReorderAlgorithm().simulateSeam( 101 () -> super.createAreaForResize(finalCellX, cellY, spanX, spanY, dragView, 102 direction, commit)); 103 } 104 105 @Override performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, View dragView, int[] result, int[] resultSpan, int mode)106 int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, 107 View dragView, int[] result, int[] resultSpan, int mode) { 108 if (pixelX >= getWidth() / 2) { 109 pixelX += getCellWidth(); 110 } 111 return super.performReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, dragView, 112 result, resultSpan, mode); 113 } 114 115 @Override createReorderAlgorithm()116 public MulticellReorderAlgorithm createReorderAlgorithm() { 117 return new MulticellReorderAlgorithm(this); 118 } 119 120 @Override copyCurrentStateToSolution(ItemConfiguration solution, boolean temp)121 public void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) { 122 int childCount = mShortcutsAndWidgets.getChildCount(); 123 for (int i = 0; i < childCount; i++) { 124 View child = mShortcutsAndWidgets.getChildAt(i); 125 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 126 int seamOffset = lp.getCellX() >= mCountX / 2 && lp.canReorder ? 1 : 0; 127 CellAndSpan c = new CellAndSpan(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, 128 lp.cellVSpan); 129 solution.add(child, c); 130 } 131 } 132 133 @Override getUnusedHorizontalSpace()134 public int getUnusedHorizontalSpace() { 135 return (int) Math.ceil( 136 (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth) 137 - ((mCountX - 1) * mBorderSpace.x)) / 2f); 138 } 139 140 @Override onDraw(Canvas canvas)141 protected void onDraw(Canvas canvas) { 142 if (mLeftBackground.getAlpha() > 0) { 143 mLeftBackground.setState(mBackground.getState()); 144 mLeftBackground.draw(canvas); 145 } 146 if (mRightBackground.getAlpha() > 0) { 147 mRightBackground.setState(mBackground.getState()); 148 mRightBackground.draw(canvas); 149 } 150 151 super.onDraw(canvas); 152 } 153 154 @Override updateBgAlpha()155 protected void updateBgAlpha() { 156 mLeftBackground.setAlpha((int) (mSpringLoadedProgress * 255)); 157 mRightBackground.setAlpha((int) (mSpringLoadedProgress * 255)); 158 } 159 160 @Override onLayout(boolean changed, int l, int t, int r, int b)161 protected void onLayout(boolean changed, int l, int t, int r, int b) { 162 super.onLayout(changed, l, t, r, b); 163 Rect rect = mBackground.getBounds(); 164 mLeftBackground.setBounds(rect.left, rect.top, rect.right / 2 - 20, rect.bottom); 165 mRightBackground.setBounds(rect.right / 2 + 20, rect.top, rect.right, rect.bottom); 166 } 167 setCountX(int countX)168 public void setCountX(int countX) { 169 mCountX = countX; 170 } 171 setCountY(int countY)172 public void setCountY(int countY) { 173 mCountY = countY; 174 } 175 setOccupied(GridOccupancy occupied)176 public void setOccupied(GridOccupancy occupied) { 177 mOccupied = occupied; 178 } 179 isSeamWasAdded()180 public boolean isSeamWasAdded() { 181 return mSeamWasAdded; 182 } 183 setSeamWasAdded(boolean seamWasAdded)184 public void setSeamWasAdded(boolean seamWasAdded) { 185 mSeamWasAdded = seamWasAdded; 186 } 187 } 188