blob: 0d59848bfa5fea09860c2f9ef9e7aff4eb24a6d0 [file] [log] [blame]
Sebastian Franco09589322022-11-02 15:25:58 -07001/*
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 */
16package com.android.launcher3;
17
18import android.content.Context;
19import android.graphics.Canvas;
20import android.graphics.Rect;
21import android.graphics.drawable.Drawable;
22import android.util.AttributeSet;
23import android.view.View;
24
25import com.android.launcher3.celllayout.CellLayoutLayoutParams;
26import com.android.launcher3.util.CellAndSpan;
27import com.android.launcher3.util.GridOccupancy;
28
29import java.util.function.Supplier;
30
31/**
32 * CellLayout that simulates a split in the middle for use in foldable devices.
33 */
34public class MultipageCellLayout extends CellLayout {
35
36 private final Drawable mLeftBackground;
37 private final Drawable mRightBackground;
38
39 private View mSeam;
40
Sebastian Franco317c4fa2023-02-21 17:01:24 -080041 private boolean mSeamWasAdded = false;
42
Sebastian Franco09589322022-11-02 15:25:58 -070043 public MultipageCellLayout(Context context) {
44 this(context, null);
45 }
46
47 public MultipageCellLayout(Context context, AttributeSet attrs) {
48 this(context, attrs, 0);
49 }
50
51 public MultipageCellLayout(Context context, AttributeSet attrs, int defStyle) {
52 super(context, attrs, defStyle);
53 mLeftBackground = getContext().getDrawable(R.drawable.bg_celllayout);
54 mLeftBackground.setCallback(this);
55 mLeftBackground.setAlpha(0);
56
57 mRightBackground = getContext().getDrawable(R.drawable.bg_celllayout);
58 mRightBackground.setCallback(this);
59 mRightBackground.setAlpha(0);
60
61 DeviceProfile deviceProfile = mActivity.getDeviceProfile();
62
63 mCountX = deviceProfile.inv.numColumns * 2;
64 mCountY = deviceProfile.inv.numRows;
65 mSeam = new View(getContext());
66 setGridSize(mCountX, mCountY);
67 }
68
69 @Override
Sebastian Franco317c4fa2023-02-21 17:01:24 -080070 boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, View dragView,
71 int[] direction, boolean commit) {
Sebastian Francoee1baba2023-02-22 11:23:41 -080072 // Add seam to x position
73 if (cellX > mCountX / 2) {
74 cellX++;
75 }
76 int finalCellX = cellX;
Sebastian Franco317c4fa2023-02-21 17:01:24 -080077 return simulateSeam(
Sebastian Francoee1baba2023-02-22 11:23:41 -080078 () -> super.createAreaForResize(finalCellX, cellY, spanX, spanY, dragView,
79 direction, commit));
Sebastian Franco317c4fa2023-02-21 17:01:24 -080080 }
81
82 @Override
Sebastian Franco09589322022-11-02 15:25:58 -070083 ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
84 int spanX, int spanY) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -080085 return removeSeamFromSolution(simulateSeam(
Sebastian Franco09589322022-11-02 15:25:58 -070086 () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
Sebastian Franco317c4fa2023-02-21 17:01:24 -080087 spanY)));
Sebastian Franco09589322022-11-02 15:25:58 -070088 }
89
90 @Override
91 protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
92 int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
93 ItemConfiguration solution) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -080094 return removeSeamFromSolution(simulateSeam(
Sebastian Franco09589322022-11-02 15:25:58 -070095 () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
Sebastian Franco317c4fa2023-02-21 17:01:24 -080096 direction, dragView, decX, solution)));
Sebastian Franco09589322022-11-02 15:25:58 -070097 }
98
99 @Override
100 public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, int spanY,
101 View dragView) {
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800102 return removeSeamFromSolution(simulateSeam(
103 () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
Sebastian Franco09589322022-11-02 15:25:58 -0700104 }
105
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800106 void addSeam() {
Sebastian Franco09589322022-11-02 15:25:58 -0700107 CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mCountX / 2, 0, 1, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800108 mSeamWasAdded = true;
Sebastian Franco09589322022-11-02 15:25:58 -0700109 lp.canReorder = false;
110 mCountX++;
111 mShortcutsAndWidgets.addViewInLayout(mSeam, lp);
Sebastian Franco09589322022-11-02 15:25:58 -0700112 mOccupied = createGridOccupancy();
113 mTmpOccupied = new GridOccupancy(mCountX, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800114 }
Sebastian Franco09589322022-11-02 15:25:58 -0700115
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800116 void removeSeam() {
Sebastian Franco09589322022-11-02 15:25:58 -0700117 mCountX--;
118 mShortcutsAndWidgets.removeViewInLayout(mSeam);
Sebastian Franco09589322022-11-02 15:25:58 -0700119 mTmpOccupied = new GridOccupancy(mCountX, mCountY);
Sebastian Franco317c4fa2023-02-21 17:01:24 -0800120 mSeamWasAdded = false;
121 }
122
123 protected <T> T simulateSeam(Supplier<T> f) {
124 if (mSeamWasAdded) {
125 return f.get();
126 }
127 GridOccupancy auxGrid = mOccupied;
128 addSeam();
129 T res = f.get();
130 removeSeam();
131 mOccupied = auxGrid;
Sebastian Franco09589322022-11-02 15:25:58 -0700132 return res;
133 }
134
135 private ItemConfiguration removeSeamFromSolution(ItemConfiguration solution) {
136 solution.map.forEach((view, cell) -> cell.cellX = cell.cellX > mCountX / 2
137 ? cell.cellX - 1 : cell.cellX);
138 solution.cellX = solution.cellX > mCountX / 2 ? solution.cellX - 1 : solution.cellX;
139 return solution;
140 }
141
142 GridOccupancy createGridOccupancy() {
143 GridOccupancy grid = new GridOccupancy(mCountX, mCountY);
144 for (int i = 0; i < mShortcutsAndWidgets.getChildCount(); i++) {
145 View view = mShortcutsAndWidgets.getChildAt(i);
146 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
147 int seamOffset = lp.getCellX() >= mCountX / 2 && lp.canReorder ? 1 : 0;
148 grid.markCells(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, lp.cellVSpan,
149 true);
150 }
151 return grid;
152 }
153
154 @Override
155 protected void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
156 int childCount = mShortcutsAndWidgets.getChildCount();
157 for (int i = 0; i < childCount; i++) {
158 View child = mShortcutsAndWidgets.getChildAt(i);
159 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
160 int seamOffset = lp.getCellX() >= mCountX / 2 && lp.canReorder ? 1 : 0;
161 CellAndSpan c = new CellAndSpan(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan,
162 lp.cellVSpan);
163 solution.add(child, c);
164 }
165 }
166
167 @Override
168 protected void onDraw(Canvas canvas) {
169 if (mLeftBackground.getAlpha() > 0) {
170 mLeftBackground.setState(mBackground.getState());
171 mLeftBackground.draw(canvas);
172 }
173 if (mRightBackground.getAlpha() > 0) {
174 mRightBackground.setState(mBackground.getState());
175 mRightBackground.draw(canvas);
176 }
177
178 super.onDraw(canvas);
179 }
180
181 @Override
182 protected void updateBgAlpha() {
183 mLeftBackground.setAlpha((int) (mSpringLoadedProgress * 255));
184 mRightBackground.setAlpha((int) (mSpringLoadedProgress * 255));
185 }
186
187 @Override
188 protected void onLayout(boolean changed, int l, int t, int r, int b) {
189 super.onLayout(changed, l, t, r, b);
190 Rect rect = mBackground.getBounds();
191 mLeftBackground.setBounds(rect.left, rect.top, rect.right / 2 - 20, rect.bottom);
192 mRightBackground.setBounds(rect.right / 2 + 20, rect.top, rect.right, rect.bottom);
193 }
194}