1 /* 2 * Copyright (C) 2023 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 17 package com.android.car.carlauncher.pagination; 18 19 import static com.android.car.carlauncher.AppGridConstants.AppItemBoundDirection; 20 import static com.android.car.carlauncher.AppGridConstants.PageOrientation; 21 import static com.android.car.carlauncher.AppGridConstants.isHorizontal; 22 23 import android.view.View; 24 25 /** 26 * Helper class that handles all page rounding logic. 27 */ 28 public class PageIndexingHelper { 29 private final int mNumOfCols; 30 private final int mNumOfRows; 31 @PageOrientation 32 private final int mPageOrientation; 33 34 private int mLayoutDirection; 35 PageIndexingHelper(int numOfCols, int numOfRows, @PageOrientation int orientation)36 public PageIndexingHelper(int numOfCols, int numOfRows, @PageOrientation int orientation) { 37 mNumOfCols = numOfCols; 38 mNumOfRows = numOfRows; 39 mPageOrientation = orientation; 40 } 41 42 /** 43 * Layout direction needs to be reset onResume as to not crash when user switches to another 44 * language with different layout direction. 45 */ setLayoutDirection(int layoutDirection)46 public void setLayoutDirection(int layoutDirection) { 47 mLayoutDirection = layoutDirection; 48 } 49 50 /** 51 * Returns the direction of the offset to add to the app item at the given grid position. 52 * 53 * For example, when there are 5 app items per column when using horizontal paging, the 54 * 1st column app item should have padding to the left and 4th column app item should have 55 * padding to the right. 56 */ 57 @AppItemBoundDirection getOffsetBoundDirection(int gridPosition)58 public int getOffsetBoundDirection(int gridPosition) { 59 // TODO (b/271628061): rename gridPosition and adapterIndex 60 if (isHorizontal(mPageOrientation)) { 61 int cid = (gridPosition / mNumOfRows) % mNumOfCols; 62 if (cid == 0) { 63 return AppItemBoundDirection.LEFT; 64 } else if (cid == mNumOfCols - 1) { 65 return AppItemBoundDirection.RIGHT; 66 } 67 } else { 68 int rid = (gridPosition / mNumOfCols) % mNumOfRows; 69 if (rid == 0) { 70 return AppItemBoundDirection.TOP; 71 } else if (rid == mNumOfRows - 1) { 72 return AppItemBoundDirection.BOTTOM; 73 } 74 } 75 return AppItemBoundDirection.NONE; 76 } 77 78 /** 79 * Grid position refers to the default position in a RecyclerView used to draw out a horizontal 80 * grid layout, shown below. 81 * 82 * This is the value returned by the default ViewHolder.getAbsoluteAdapterPosition(). 83 */ gridPositionToAdaptorIndex(int position)84 public int gridPositionToAdaptorIndex(int position) { 85 if (!isHorizontal(mPageOrientation)) { 86 if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) { 87 int cid = position % mNumOfCols; 88 // column order swap 89 position = (position - cid) + (mNumOfCols - cid - 1); 90 } 91 return position; 92 } 93 int positionOnPage = position % (mNumOfCols * mNumOfRows); 94 // page the item resides on 95 int pid = position / (mNumOfCols * mNumOfRows); 96 // row of the item, in matrix order 97 int rid = positionOnPage % mNumOfRows; 98 // column of the item, in matrix order / LTR order 99 int cid = positionOnPage / mNumOfRows; 100 101 if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) { 102 cid = mNumOfCols - cid - 1; 103 } 104 return (pid * mNumOfRows * mNumOfCols) + (rid * mNumOfCols) + cid; 105 } 106 107 /** 108 * Adapter index refers to the "business logic" index, which is the order which the users will 109 * read the app in their language (either LTR or RTL on each page) 110 */ adaptorIndexToGridPosition(int index)111 public int adaptorIndexToGridPosition(int index) { 112 if (!isHorizontal(mPageOrientation)) { 113 if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) { 114 int cid = index % mNumOfCols; 115 // column order swap 116 index = (index - cid) + (mNumOfCols - cid - 1); 117 } 118 return index; 119 } 120 int indexOnPage = index % (mNumOfCols * mNumOfRows); 121 // page the item resides on 122 int pid = index / (mNumOfCols * mNumOfRows); 123 // row of the item, in matrix order 124 int rid = indexOnPage / mNumOfCols; 125 // column of the item, in matrix order / LTR order 126 int cid = indexOnPage % mNumOfCols; 127 128 if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) { 129 cid = mNumOfCols - cid - 1; 130 } 131 return (pid * mNumOfRows * mNumOfCols) + (cid * mNumOfRows) + rid; 132 } 133 134 /** 135 * Returns the grid position of the FIRST item on the page. The result is always the same in RTL 136 * and LTR since the first and last index of every page is always mirrored. 137 * 138 * * Grid Position example - Horizontal 139 * * |[0] 3 6 9 12 |[15] 18 21 24 27 | 140 * * | 1 4 7 10 13 | 16 19 22 25 28 | 141 * * | 2 5 8 11 14 | 17 20 23 26 29 | 142 * 143 * * Grid Position example - Vertical 144 * * |[1] 2 3 4 5 | 145 * * | 6 7 8 9 10 | 146 * * | 11 12 13 14 15 | 147 * * __________________ 148 * * |[16] 17 18 19 20 | 149 * * | 21 22 23 24 25 | 150 * * | 26 27 28 29 30 | 151 */ roundToFirstIndexOnPage(int gridPosition)152 public int roundToFirstIndexOnPage(int gridPosition) { 153 int pidRoundedDown = gridPosition / (mNumOfCols * mNumOfRows); 154 return pidRoundedDown * (mNumOfCols * mNumOfRows); 155 } 156 157 /** 158 * Returns the grid position of the LAST item on the page. The result is always the same in RTL 159 * and LTR since the first and last index of every page is always mirrored. 160 * 161 * * Grid position example - horizontal 162 * * | 0 3 6 9 12 | 15 18 21 24 27 | 163 * * | 1 4 7 10 13 | 16 19 22 25 28 | 164 * * | 2 5 8 11 [14] | 17 20 23 26 [29] | 165 * 166 * * Grid position example - Vertical 167 * * | 1 2 3 4 5 | 168 * * | 6 7 8 9 10 | 169 * * | 11 12 13 14 [15] | 170 * * 171 * * | 16 17 18 19 20 | 172 * * | 21 22 23 24 25 | 173 * * | 26 27 28 29 [30] | 174 */ roundToLastIndexOnPage(int gridPosition)175 public int roundToLastIndexOnPage(int gridPosition) { 176 int pidRoundedUp = gridPosition / (mNumOfCols * mNumOfRows) + 1; 177 return pidRoundedUp * (mNumOfCols * mNumOfRows) - 1; 178 } 179 } 180