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.chassis.car.ui.plugin.recyclerview; 18 19 import static androidx.recyclerview.widget.RecyclerView.VERTICAL; 20 21 import android.content.Context; 22 import android.view.View; 23 24 import androidx.annotation.NonNull; 25 import androidx.annotation.Nullable; 26 import androidx.recyclerview.widget.GridLayoutManager; 27 import androidx.recyclerview.widget.LinearLayoutManager; 28 import androidx.recyclerview.widget.RecyclerView; 29 import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener; 30 import androidx.recyclerview.widget.RecyclerView.ViewHolder; 31 32 import com.android.car.ui.plugin.oemapis.recyclerview.AdapterOEMV1; 33 import com.android.car.ui.plugin.oemapis.recyclerview.LayoutStyleOEMV1; 34 import com.android.car.ui.plugin.oemapis.recyclerview.OnChildAttachStateChangeListenerOEMV1; 35 import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewAttributesOEMV1; 36 import com.android.car.ui.plugin.oemapis.recyclerview.RecyclerViewOEMV2; 37 import com.android.car.ui.plugin.oemapis.recyclerview.ViewHolderOEMV1; 38 import com.android.car.ui.recyclerview.CarUiRecyclerView; 39 import com.android.car.ui.recyclerview.CarUiRecyclerViewImpl; 40 import com.android.car.ui.recyclerview.RecyclerViewAdapterV1; 41 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * Adapts a {@link RecyclerViewAdapterV1} into a {@link RecyclerViewOEMV2}. 47 */ 48 public class RecyclerViewAdapterProxyV2 implements RecyclerViewOEMV2 { 49 private Context mPluginContext; 50 private CarUiRecyclerViewImpl mRecyclerView; 51 RecyclerViewAdapterProxyV2(Context pluginContext, CarUiRecyclerViewImpl recyclerView, RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1)52 public RecyclerViewAdapterProxyV2(Context pluginContext, CarUiRecyclerViewImpl recyclerView, 53 RecyclerViewAttributesOEMV1 recyclerViewAttributesOEMV1) { 54 mPluginContext = pluginContext; 55 mRecyclerView = recyclerView; 56 setLayoutStyle(recyclerViewAttributesOEMV1.getLayoutStyle()); 57 } 58 59 @NonNull 60 private final List<OnScrollListenerOEMV2> mScrollListeners = new ArrayList<>(); 61 62 @NonNull 63 private final CarUiRecyclerView.OnScrollListener mOnScrollListener = 64 new CarUiRecyclerView.OnScrollListener() { 65 @Override 66 public void onScrolled(@NonNull CarUiRecyclerView recyclerView, int dx, int dy) { 67 for (OnScrollListenerOEMV2 listener : mScrollListeners) { 68 listener.onScrolled(RecyclerViewAdapterProxyV2.this, dx, dy); 69 } 70 } 71 72 @Override 73 public void onScrollStateChanged(@NonNull CarUiRecyclerView recyclerView, 74 int newState) { 75 for (OnScrollListenerOEMV2 listener : mScrollListeners) { 76 listener.onScrollStateChanged(RecyclerViewAdapterProxyV2.this, 77 toInternalScrollState(newState)); 78 } 79 } 80 }; 81 @NonNull 82 private final List<OnChildAttachStateChangeListenerOEMV1> mOnChildAttachStateChangeListeners = 83 new ArrayList<>(); 84 85 @NonNull 86 private final OnChildAttachStateChangeListener mOnChildAttachStateChangeListener = 87 new OnChildAttachStateChangeListener() { 88 @Override 89 public void onChildViewAttachedToWindow(@NonNull View view) { 90 for (OnChildAttachStateChangeListenerOEMV1 listener : 91 mOnChildAttachStateChangeListeners) { 92 listener.onChildViewAttachedToWindow(view); 93 } 94 } 95 96 @Override 97 public void onChildViewDetachedFromWindow(@NonNull View view) { 98 for (OnChildAttachStateChangeListenerOEMV1 listener : 99 mOnChildAttachStateChangeListeners) { 100 listener.onChildViewDetachedFromWindow(view); 101 } 102 } 103 }; 104 toInternalScrollState(int state)105 private static int toInternalScrollState(int state) { 106 /* default to RecyclerView.SCROLL_STATE_IDLE */ 107 int internalState = RecyclerViewOEMV2.SCROLL_STATE_IDLE; 108 switch (state) { 109 case RecyclerView.SCROLL_STATE_DRAGGING: 110 internalState = RecyclerViewOEMV2.SCROLL_STATE_DRAGGING; 111 break; 112 case RecyclerView.SCROLL_STATE_SETTLING: 113 internalState = RecyclerViewOEMV2.SCROLL_STATE_SETTLING; 114 break; 115 } 116 return internalState; 117 } 118 119 120 @Override setAdapter(AdapterOEMV1<V> adapterOEMV1)121 public <V extends ViewHolderOEMV1> void setAdapter(AdapterOEMV1<V> adapterOEMV1) { 122 if (adapterOEMV1 == null) { 123 mRecyclerView.setAdapter(null); 124 } else { 125 mRecyclerView.setAdapter(new RVAdapterWrapper(adapterOEMV1)); 126 } 127 } 128 129 @Override addOnScrollListener(@onNull OnScrollListenerOEMV2 onScrollListenerOEMV2)130 public void addOnScrollListener(@NonNull OnScrollListenerOEMV2 onScrollListenerOEMV2) { 131 if (mScrollListeners.isEmpty()) { 132 mRecyclerView.addOnScrollListener(mOnScrollListener); 133 } 134 mScrollListeners.add(onScrollListenerOEMV2); 135 } 136 137 @Override removeOnScrollListener(@onNull OnScrollListenerOEMV2 onScrollListenerOEMV2)138 public void removeOnScrollListener(@NonNull OnScrollListenerOEMV2 onScrollListenerOEMV2) { 139 mScrollListeners.remove(onScrollListenerOEMV2); 140 if (mScrollListeners.isEmpty()) { 141 mRecyclerView.removeOnScrollListener(mOnScrollListener); 142 } 143 } 144 145 @Override clearOnScrollListeners()146 public void clearOnScrollListeners() { 147 if (!mScrollListeners.isEmpty()) { 148 mScrollListeners.clear(); 149 mRecyclerView.clearOnScrollListeners(); 150 } 151 } 152 153 @Override scrollToPosition(int i)154 public void scrollToPosition(int i) { 155 mRecyclerView.scrollToPosition(i); 156 } 157 158 @Override smoothScrollBy(int i, int i1)159 public void smoothScrollBy(int i, int i1) { 160 mRecyclerView.smoothScrollBy(i, i1); 161 } 162 163 @Override smoothScrollToPosition(int i)164 public void smoothScrollToPosition(int i) { 165 mRecyclerView.smoothScrollToPosition(i); 166 } 167 168 @Override setHasFixedSize(boolean b)169 public void setHasFixedSize(boolean b) { 170 mRecyclerView.setHasFixedSize(b); 171 } 172 173 @Override hasFixedSize()174 public boolean hasFixedSize() { 175 return mRecyclerView.hasFixedSize(); 176 } 177 178 @Nullable 179 private LayoutStyleOEMV1 mLayoutStyle; 180 @Override setLayoutStyle(LayoutStyleOEMV1 layoutStyleOEMV1)181 public void setLayoutStyle(LayoutStyleOEMV1 layoutStyleOEMV1) { 182 mLayoutStyle = layoutStyleOEMV1; 183 184 int orientation = layoutStyleOEMV1 == null ? VERTICAL : layoutStyleOEMV1.getOrientation(); 185 boolean reverseLayout = layoutStyleOEMV1 != null && layoutStyleOEMV1.getReverseLayout(); 186 187 if (layoutStyleOEMV1 == null 188 || layoutStyleOEMV1.getLayoutType() == LayoutStyleOEMV1.LAYOUT_TYPE_LINEAR) { 189 mRecyclerView.setLayoutManager(new LinearLayoutManager(mPluginContext, 190 orientation, 191 reverseLayout)); 192 } else { 193 GridLayoutManager glm = new GridLayoutManager(mPluginContext, 194 layoutStyleOEMV1.getSpanCount(), 195 orientation, 196 reverseLayout); 197 glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 198 @Override 199 public int getSpanSize(int position) { 200 return layoutStyleOEMV1.getSpanSize(position); 201 } 202 }); 203 mRecyclerView.setLayoutManager(glm); 204 } 205 } 206 207 @Override getLayoutStyle()208 public LayoutStyleOEMV1 getLayoutStyle() { 209 return mLayoutStyle; 210 } 211 212 @Override getView()213 public View getView() { 214 return mRecyclerView.getView(); 215 } 216 217 @Override setPadding(int i, int i1, int i2, int i3)218 public void setPadding(int i, int i1, int i2, int i3) { 219 mRecyclerView.setPadding(i, i1, i2, i3); 220 } 221 222 @Override setPaddingRelative(int i, int i1, int i2, int i3)223 public void setPaddingRelative(int i, int i1, int i2, int i3) { 224 mRecyclerView.setPaddingRelative(i, i1, i2, i3); 225 } 226 227 @Override setClipToPadding(boolean b)228 public void setClipToPadding(boolean b) { 229 mRecyclerView.setClipToPadding(b); 230 } 231 232 @Override findFirstCompletelyVisibleItemPosition()233 public int findFirstCompletelyVisibleItemPosition() { 234 return mRecyclerView.findFirstCompletelyVisibleItemPosition(); 235 } 236 237 @Override findFirstVisibleItemPosition()238 public int findFirstVisibleItemPosition() { 239 return mRecyclerView.findFirstVisibleItemPosition(); 240 } 241 242 @Override findLastCompletelyVisibleItemPosition()243 public int findLastCompletelyVisibleItemPosition() { 244 return mRecyclerView.findLastVisibleItemPosition(); 245 } 246 247 @Override findLastVisibleItemPosition()248 public int findLastVisibleItemPosition() { 249 return mRecyclerView.findLastVisibleItemPosition(); 250 } 251 252 @Override getScrollState()253 public int getScrollState() { 254 return toInternalScrollState(mRecyclerView.getScrollState()); 255 } 256 257 @Override setContentDescription(CharSequence charSequence)258 public void setContentDescription(CharSequence charSequence) { 259 mRecyclerView.setContentDescription(charSequence); 260 } 261 262 @Override setAlpha(float v)263 public void setAlpha(float v) { 264 mRecyclerView.setAlpha(v); 265 } 266 267 @Override getEndAfterPadding()268 public int getEndAfterPadding() { 269 return mRecyclerView.getEndAfterPadding(); 270 } 271 272 @Override getStartAfterPadding()273 public int getStartAfterPadding() { 274 return mRecyclerView.getStartAfterPadding(); 275 } 276 277 @Override getTotalSpace()278 public int getTotalSpace() { 279 return mRecyclerView.getTotalSpace(); 280 } 281 282 @Override getRecyclerViewChildCount()283 public int getRecyclerViewChildCount() { 284 return mRecyclerView.getRecyclerViewChildCount(); 285 } 286 287 @Override getRecyclerViewChildAt(int i)288 public View getRecyclerViewChildAt(int i) { 289 return mRecyclerView.getRecyclerViewChildAt(i); 290 } 291 292 @Override getRecyclerViewChildPosition(View view)293 public int getRecyclerViewChildPosition(View view) { 294 return mRecyclerView.getRecyclerViewChildPosition(view); 295 } 296 297 @Override findViewHolderForAdapterPosition(int position)298 public ViewHolderOEMV1 findViewHolderForAdapterPosition(int position) { 299 ViewHolder viewHolder = mRecyclerView.findViewHolderForAdapterPosition(position); 300 if (viewHolder instanceof RVAdapterWrapper.ViewHolderWrapper) { 301 return ((RVAdapterWrapper.ViewHolderWrapper) viewHolder).getViewHolder(); 302 } 303 return null; 304 } 305 306 @Override findViewHolderForLayoutPosition(int position)307 public ViewHolderOEMV1 findViewHolderForLayoutPosition(int position) { 308 ViewHolder viewHolder = mRecyclerView.findViewHolderForLayoutPosition(position); 309 if (viewHolder instanceof RVAdapterWrapper.ViewHolderWrapper) { 310 return ((RVAdapterWrapper.ViewHolderWrapper) viewHolder).getViewHolder(); 311 } 312 return null; 313 } 314 315 @Override addOnChildAttachStateChangeListener( OnChildAttachStateChangeListenerOEMV1 listener)316 public void addOnChildAttachStateChangeListener( 317 OnChildAttachStateChangeListenerOEMV1 listener) { 318 if (listener == null) { 319 return; 320 } 321 if (mOnChildAttachStateChangeListeners.isEmpty()) { 322 mRecyclerView.addOnChildAttachStateChangeListener(mOnChildAttachStateChangeListener); 323 } 324 mOnChildAttachStateChangeListeners.add(listener); 325 } 326 327 @Override removeOnChildAttachStateChangeListener( OnChildAttachStateChangeListenerOEMV1 listener)328 public void removeOnChildAttachStateChangeListener( 329 OnChildAttachStateChangeListenerOEMV1 listener) { 330 if (listener == null) { 331 return; 332 } 333 mOnChildAttachStateChangeListeners.remove(listener); 334 if (mOnChildAttachStateChangeListeners.isEmpty()) { 335 mRecyclerView.removeOnChildAttachStateChangeListener(mOnChildAttachStateChangeListener); 336 } 337 } 338 339 @Override clearOnChildAttachStateChangeListener()340 public void clearOnChildAttachStateChangeListener() { 341 if (!mOnChildAttachStateChangeListeners.isEmpty()) { 342 mOnChildAttachStateChangeListeners.clear(); 343 mRecyclerView.clearOnChildAttachStateChangeListeners(); 344 } 345 } 346 347 @Override getChildLayoutPosition(View view)348 public int getChildLayoutPosition(View view) { 349 return mRecyclerView.getChildLayoutPosition(view); 350 } 351 352 @Override getDecoratedStart(View view)353 public int getDecoratedStart(View view) { 354 return mRecyclerView.getDecoratedStart(view); 355 } 356 357 @Override getDecoratedEnd(View view)358 public int getDecoratedEnd(View view) { 359 return mRecyclerView.getDecoratedEnd(view); 360 } 361 362 @Override getDecoratedMeasuredHeight(View view)363 public int getDecoratedMeasuredHeight(View view) { 364 return mRecyclerView.getDecoratedMeasuredHeight(view); 365 } 366 367 @Override getDecoratedMeasuredWidth(View view)368 public int getDecoratedMeasuredWidth(View view) { 369 return mRecyclerView.getDecoratedMeasuredWidth(view); 370 } 371 372 @Override getDecoratedMeasurementInOther(View view)373 public int getDecoratedMeasurementInOther(View view) { 374 return mRecyclerView.getDecoratedMeasurementInOther(view); 375 } 376 377 @Override getDecoratedMeasurement(View view)378 public int getDecoratedMeasurement(View view) { 379 return mRecyclerView.getDecoratedMeasurement(view); 380 } 381 382 @Override findViewByPosition(int i)383 public View findViewByPosition(int i) { 384 return mRecyclerView.findViewByPosition(i); 385 } 386 387 @Override isComputingLayout()388 public boolean isComputingLayout() { 389 return !mRecyclerView.isLayoutCompleted(); 390 } 391 392 @Override addOnLayoutCompleteListener(@ullable Runnable runnable)393 public void addOnLayoutCompleteListener(@Nullable Runnable runnable) { 394 mRecyclerView.addOnLayoutCompleteListener(runnable); 395 } 396 397 @Override removeOnLayoutCompleteListener(@ullable Runnable runnable)398 public void removeOnLayoutCompleteListener(@Nullable Runnable runnable) { 399 mRecyclerView.removeOnLayoutCompleteListener(runnable); 400 } 401 402 @Override scrollToPositionWithOffset(int i, int i1)403 public void scrollToPositionWithOffset(int i, int i1) { 404 mRecyclerView.scrollToPositionWithOffset(i, i1); 405 } 406 } 407