• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.ui.sharedlibrarysupport;
17 
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.text.SpannableString;
21 import android.util.AttributeSet;
22 import android.view.View;
23 
24 import androidx.annotation.NonNull;
25 import androidx.annotation.Nullable;
26 import androidx.recyclerview.widget.RecyclerView;
27 
28 import com.android.car.ui.CarUiText;
29 import com.android.car.ui.FocusArea;
30 import com.android.car.ui.FocusAreaAdapterV1;
31 import com.android.car.ui.FocusParkingView;
32 import com.android.car.ui.FocusParkingViewAdapterV1;
33 import com.android.car.ui.R;
34 import com.android.car.ui.appstyledview.AppStyledViewController;
35 import com.android.car.ui.appstyledview.AppStyledViewControllerAdapterV1;
36 import com.android.car.ui.appstyledview.AppStyledViewControllerImpl;
37 import com.android.car.ui.baselayout.Insets;
38 import com.android.car.ui.baselayout.InsetsChangedListener;
39 import com.android.car.ui.recyclerview.CarUiContentListItem;
40 import com.android.car.ui.recyclerview.CarUiHeaderListItem;
41 import com.android.car.ui.recyclerview.CarUiLayoutStyle;
42 import com.android.car.ui.recyclerview.CarUiListItem;
43 import com.android.car.ui.recyclerview.CarUiListItemAdapterAdapterV1;
44 import com.android.car.ui.recyclerview.CarUiRecyclerView;
45 import com.android.car.ui.recyclerview.CarUiRecyclerView.CarUiRecyclerViewLayout;
46 import com.android.car.ui.recyclerview.RecyclerViewAdapterV1;
47 import com.android.car.ui.sharedlibrary.oemapis.InsetsOEMV1;
48 import com.android.car.ui.sharedlibrary.oemapis.SharedLibraryFactoryOEMV1;
49 import com.android.car.ui.sharedlibrary.oemapis.appstyledview.AppStyledViewControllerOEMV1;
50 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.AdapterOEMV1;
51 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.ContentListItemOEMV1;
52 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.HeaderListItemOEMV1;
53 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.LayoutStyleOEMV1;
54 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.ListItemOEMV1;
55 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.RecyclerViewAttributesOEMV1;
56 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.RecyclerViewOEMV1;
57 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.SpanSizeLookupOEMV1;
58 import com.android.car.ui.sharedlibrary.oemapis.recyclerview.ViewHolderOEMV1;
59 import com.android.car.ui.sharedlibrary.oemapis.toolbar.ToolbarControllerOEMV1;
60 import com.android.car.ui.toolbar.ToolbarController;
61 import com.android.car.ui.toolbar.ToolbarControllerAdapterV1;
62 import com.android.car.ui.utils.CarUiUtils;
63 import com.android.car.ui.widget.CarUiTextView;
64 
65 import java.util.List;
66 import java.util.function.Consumer;
67 
68 /**
69  * This class is an wrapper around {@link SharedLibraryFactoryOEMV1} that implements {@link
70  * SharedLibraryFactory}, to provide a version-agnostic way of interfacing with the OEM's
71  * SharedLibraryFactory.
72  */
73 public final class SharedLibraryFactoryAdapterV1 implements SharedLibraryFactory {
74     SharedLibraryFactoryOEMV1 mOem;
75     SharedLibraryFactoryStub mFactoryStub;
76 
SharedLibraryFactoryAdapterV1(SharedLibraryFactoryOEMV1 oem, Context sharedLibContext)77     public SharedLibraryFactoryAdapterV1(SharedLibraryFactoryOEMV1 oem, Context sharedLibContext) {
78         mOem = oem;
79         mFactoryStub = new SharedLibraryFactoryStub(sharedLibContext);
80 
81         mOem.setRotaryFactories(
82                 c -> new FocusParkingViewAdapterV1(new FocusParkingView(c)),
83                 c -> new FocusAreaAdapterV1(new FocusArea(c)));
84     }
85 
86     @Override
87     @Nullable
installBaseLayoutAround( View contentView, InsetsChangedListener insetsChangedListener, boolean toolbarEnabled, boolean fullscreen)88     public ToolbarController installBaseLayoutAround(
89             View contentView,
90             InsetsChangedListener insetsChangedListener,
91             boolean toolbarEnabled,
92             boolean fullscreen) {
93 
94         if (!mOem.customizesBaseLayout()) {
95             return mFactoryStub.installBaseLayoutAround(contentView,
96                     insetsChangedListener, toolbarEnabled, fullscreen);
97         }
98 
99         ToolbarControllerOEMV1 toolbar = mOem.installBaseLayoutAround(contentView,
100                 insets -> insetsChangedListener.onCarUiInsetsChanged(adaptInsets(insets)),
101                 toolbarEnabled, fullscreen);
102 
103         return toolbar != null
104                 ? new ToolbarControllerAdapterV1(contentView.getContext(), toolbar)
105                 : null;
106     }
107 
108     @NonNull
109     @Override
createTextView(Context context, AttributeSet attrs)110     public CarUiTextView createTextView(Context context, AttributeSet attrs) {
111         return mFactoryStub.createTextView(context, attrs);
112     }
113 
114 
115     @Override
createAppStyledView(Context activityContext)116     public AppStyledViewController createAppStyledView(Context activityContext) {
117         AppStyledViewControllerOEMV1 appStyledViewControllerOEMV1 = mOem.createAppStyledView();
118         return appStyledViewControllerOEMV1 == null ? new AppStyledViewControllerImpl(
119                 activityContext) : new AppStyledViewControllerAdapterV1(
120                 appStyledViewControllerOEMV1);
121     }
122 
adaptInsets(InsetsOEMV1 insetsOEM)123     private Insets adaptInsets(InsetsOEMV1 insetsOEM) {
124         return new Insets(insetsOEM.getLeft(), insetsOEM.getTop(),
125                 insetsOEM.getRight(), insetsOEM.getBottom());
126     }
127 
128     @Override
createRecyclerView(@onNull Context context, @Nullable AttributeSet attrs)129     public CarUiRecyclerView createRecyclerView(@NonNull Context context,
130             @Nullable AttributeSet attrs) {
131         RecyclerViewAttributesOEMV1 oemAttrs = from(context, attrs);
132         RecyclerViewOEMV1 oemRecyclerView = mOem.createRecyclerView(context, oemAttrs);
133         if (oemRecyclerView != null) {
134             RecyclerViewAdapterV1 rv = new RecyclerViewAdapterV1(context, attrs);
135             rv.setRecyclerViewOEMV1(oemRecyclerView);
136             return rv;
137         } else {
138             return mFactoryStub.createRecyclerView(context, attrs);
139         }
140     }
141 
142     @Override
createListItemAdapter( List<? extends CarUiListItem> items)143     public RecyclerView.Adapter<? extends RecyclerView.ViewHolder> createListItemAdapter(
144             List<? extends CarUiListItem> items) {
145         List<ListItemOEMV1> oemItems = CarUiUtils.convertList(items,
146                 SharedLibraryFactoryAdapterV1::toOemListItem);
147 
148         AdapterOEMV1<? extends ViewHolderOEMV1> oemAdapter = mOem.createListItemAdapter(oemItems);
149         return oemAdapter != null ? new CarUiListItemAdapterAdapterV1(oemAdapter)
150                 : mFactoryStub.createListItemAdapter(items);
151     }
152 
from(Context context, AttributeSet attrs)153     private static RecyclerViewAttributesOEMV1 from(Context context, AttributeSet attrs) {
154         RecyclerViewAttributesOEMV1 oemAttrs = null;
155         if (attrs != null) {
156             TypedArray a = context.obtainStyledAttributes(
157                     attrs,
158                     R.styleable.CarUiRecyclerView,
159                     0,
160                     R.style.Widget_CarUi_CarUiRecyclerView);
161             final int carUiRecyclerViewLayout = a.getInt(
162                     R.styleable.CarUiRecyclerView_layoutStyle,
163                     CarUiRecyclerViewLayout.LINEAR);
164             final int spanCount = a.getInt(
165                     R.styleable.CarUiRecyclerView_numOfColumns, /* defValue= */ 1);
166             final boolean rotaryScrollEnabled = a.getBoolean(
167                     R.styleable.CarUiRecyclerView_rotaryScrollEnabled,
168                     /* defValue=*/ false);
169             final int orientation = a.getInt(
170                     R.styleable.CarUiRecyclerView_android_orientation,
171                     CarUiLayoutStyle.VERTICAL);
172             final boolean reversed = a.getBoolean(
173                     R.styleable.CarUiRecyclerView_reverseLayout, false);
174             final int size = a.getInt(R.styleable.CarUiRecyclerView_carUiSize,
175                     CarUiRecyclerView.SIZE_LARGE);
176             a.recycle();
177 
178             final LayoutStyleOEMV1 layoutStyle = new LayoutStyleOEMV1() {
179                 @Override
180                 public int getSpanCount() {
181                     return spanCount;
182                 }
183 
184                 @Override
185                 public int getLayoutType() {
186                     switch (carUiRecyclerViewLayout) {
187                         case CarUiRecyclerViewLayout.GRID:
188                             return LayoutStyleOEMV1.LAYOUT_TYPE_GRID;
189                         case CarUiRecyclerViewLayout.LINEAR:
190                         default:
191                             return LayoutStyleOEMV1.LAYOUT_TYPE_LINEAR;
192                     }
193                 }
194 
195                 @Override
196                 public int getOrientation() {
197                     switch (orientation) {
198                         case CarUiLayoutStyle.HORIZONTAL:
199                             return LayoutStyleOEMV1.ORIENTATION_HORIZONTAL;
200                         case CarUiLayoutStyle.VERTICAL:
201                         default:
202                             return LayoutStyleOEMV1.ORIENTATION_VERTICAL;
203                     }
204                 }
205 
206                 @Override
207                 public boolean getReverseLayout() {
208                     return reversed;
209                 }
210 
211                 @Override
212                 public SpanSizeLookupOEMV1 getSpanSizeLookup() {
213                     // This can be set via setLayoutStyle API later.
214                     return null;
215                 }
216             };
217 
218             oemAttrs = new RecyclerViewAttributesOEMV1() {
219                 @Override
220                 public boolean isRotaryScrollEnabled() {
221                     return rotaryScrollEnabled;
222                 }
223 
224                 @Override
225                 public int getSize() {
226                     switch (size) {
227                         case CarUiRecyclerView.SIZE_SMALL:
228                             return RecyclerViewAttributesOEMV1.SIZE_SMALL;
229                         case CarUiRecyclerView.SIZE_MEDIUM:
230                             return RecyclerViewAttributesOEMV1.SIZE_MEDIUM;
231                         case CarUiRecyclerView.SIZE_LARGE:
232                         default:
233                             return RecyclerViewAttributesOEMV1.SIZE_LARGE;
234                     }
235                 }
236 
237                 @Override
238                 public LayoutStyleOEMV1 getLayoutStyle() {
239                     return layoutStyle;
240                 }
241             };
242         }
243         return oemAttrs;
244     }
245 
toOemListItem(CarUiListItem item)246     private static ListItemOEMV1 toOemListItem(CarUiListItem item) {
247         if (item instanceof CarUiHeaderListItem) {
248             CarUiHeaderListItem header = (CarUiHeaderListItem) item;
249             return new HeaderListItemOEMV1.Builder(new SpannableString(header.getTitle()))
250                     .setBody(new SpannableString(header.getBody()))
251                     .build();
252         } else if (item instanceof CarUiContentListItem) {
253             CarUiContentListItem contentItem = (CarUiContentListItem) item;
254 
255             ContentListItemOEMV1.Builder builder = new ContentListItemOEMV1.Builder(
256                     toOemListItemAction(contentItem.getAction()));
257 
258             if (contentItem.getTitle() != null) {
259                 builder.setTitle(
260                         new SpannableString(contentItem.getTitle().getPreferredText()));
261             }
262 
263             if (contentItem.getBody() != null) {
264                 builder.setBody(new SpannableString(
265                         CarUiText.combineMultiLine(contentItem.getBody())));
266             }
267 
268             builder.setIcon(contentItem.getIcon(),
269                     toOemListItemIconType(contentItem.getPrimaryIconType()));
270 
271             if (contentItem.getAction() == CarUiContentListItem.Action.ICON) {
272                 Consumer<ContentListItemOEMV1> listener =
273                         contentItem.getSupplementalIconOnClickListener() != null
274                                 ? oemItem ->
275                                 contentItem.getSupplementalIconOnClickListener().onClick(
276                                         contentItem) : null;
277                 builder.setSupplementalIcon(contentItem.getSupplementalIcon(), listener);
278             }
279 
280             if (contentItem.getOnClickListener() != null) {
281                 Consumer<ContentListItemOEMV1> listener =
282                         contentItem.getOnClickListener() != null
283                                 ? oemItem ->
284                                 contentItem.getOnClickListener().onClick(contentItem) : null;
285                 builder.setOnItemClickedListener(listener);
286             }
287 
288             builder.setOnCheckedChangeListener(
289                     oemItem -> contentItem.setChecked(oemItem.isChecked()))
290                     .setActionDividerVisible(contentItem.isActionDividerVisible())
291                     .setEnabled(contentItem.isEnabled())
292                     .setChecked(contentItem.isChecked())
293                     .setActivated(contentItem.isActivated());
294             return builder.build();
295         } else {
296             throw new IllegalStateException("Unexpected list item type");
297         }
298     }
299 
toOemListItemAction( CarUiContentListItem.Action action)300     private static ContentListItemOEMV1.Action toOemListItemAction(
301             CarUiContentListItem.Action action) {
302         switch (action) {
303             case NONE:
304                 return ContentListItemOEMV1.Action.NONE;
305             case SWITCH:
306                 return ContentListItemOEMV1.Action.SWITCH;
307             case CHECK_BOX:
308                 return ContentListItemOEMV1.Action.CHECK_BOX;
309             case RADIO_BUTTON:
310                 return ContentListItemOEMV1.Action.RADIO_BUTTON;
311             case ICON:
312                 return ContentListItemOEMV1.Action.ICON;
313             case CHEVRON:
314                 return ContentListItemOEMV1.Action.CHEVRON;
315             default:
316                 throw new IllegalStateException("Unexpected list item action type");
317         }
318     }
319 
toOemListItemIconType( CarUiContentListItem.IconType iconType)320     private static ContentListItemOEMV1.IconType toOemListItemIconType(
321             CarUiContentListItem.IconType iconType) {
322         switch (iconType) {
323             case CONTENT:
324                 return ContentListItemOEMV1.IconType.CONTENT;
325             case STANDARD:
326                 return ContentListItemOEMV1.IconType.STANDARD;
327             case AVATAR:
328                 return ContentListItemOEMV1.IconType.AVATAR;
329             default:
330                 throw new IllegalStateException("Unexpected list item icon type");
331         }
332     }
333 }
334