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.core; 17 18 import static com.android.car.ui.core.BaseLayoutController.getBaseLayoutController; 19 20 import android.app.Activity; 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.RecyclerView; 27 28 import com.android.car.ui.R; 29 import com.android.car.ui.baselayout.Insets; 30 import com.android.car.ui.baselayout.InsetsChangedListener; 31 import com.android.car.ui.recyclerview.CarUiListItem; 32 import com.android.car.ui.sharedlibrarysupport.SharedLibraryFactorySingleton; 33 import com.android.car.ui.toolbar.ToolbarController; 34 35 import java.util.List; 36 import java.util.Objects; 37 38 /** 39 * Public interface for general CarUi static functions. 40 */ 41 public class CarUi { 42 43 /** Prevent instantiating this class */ CarUi()44 private CarUi() {} 45 46 /** 47 * Gets a CarUi component, such as {@link com.android.car.ui.button.CarUiButton}, from the 48 * view hierarchy. The interfaces for these components don't extend View, so you can't 49 * get them through findViewById(). 50 * 51 * @param view The parent view. Its descendants will be searched for the component. 52 * @param id The id of the component. 53 * @param <T> The resulting type of the component, such as 54 * {@link com.android.car.ui.button.CarUiButton} 55 * @return The component found, or null. 56 */ 57 @Nullable 58 @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) findCarUiComponentById(@ullable View view, int id)59 public static <T> T findCarUiComponentById(@Nullable View view, int id) { 60 if (view == null) { 61 return null; 62 } 63 View componentView = view.findViewById(id); 64 return componentView != null 65 ? (T) componentView.getTag(R.id.car_ui_component_reference) 66 : null; 67 } 68 69 /** 70 * Same as {@link #findCarUiComponentById(View, int)}, but will throw an exception 71 * if the result is null. 72 */ 73 @NonNull requireCarUiComponentById(View view, int id)74 public static <T> T requireCarUiComponentById(View view, int id) { 75 return Objects.requireNonNull(findCarUiComponentById(view, id)); 76 } 77 78 /** 79 * Gets the {@link ToolbarController} for an activity. Requires that the Activity uses 80 * Theme.CarUi.WithToolbar, or otherwise sets carUiBaseLayout and carUiToolbar to true. 81 * 82 * See also: {@link #requireToolbar(Activity)} 83 */ 84 @Nullable getToolbar(@ullable Activity activity)85 public static ToolbarController getToolbar(@Nullable Activity activity) { 86 BaseLayoutController controller = getBaseLayoutController(activity); 87 if (controller != null) { 88 return controller.getToolbarController(); 89 } 90 return null; 91 } 92 93 /** 94 * Use this method to create an instance of a {@link RecyclerView.Adapter} for a list of {@link 95 * CarUiListItem} objects. 96 */ createListItemAdapter( Context context, List<? extends CarUiListItem> items)97 public static RecyclerView.Adapter<? extends RecyclerView.ViewHolder> createListItemAdapter( 98 Context context, List<? extends CarUiListItem> items) { 99 return SharedLibraryFactorySingleton.get(context).createListItemAdapter(items); 100 } 101 102 103 /** 104 * Gets the {@link ToolbarController} for an activity. Requires that the Activity uses 105 * Theme.CarUi.WithToolbar, or otherwise sets carUiBaseLayout and carUiToolbar to true. 106 * 107 * <p>See also: {@link #getToolbar(Activity)} 108 * 109 * @throws IllegalArgumentException When the CarUi Toolbar cannot be found. 110 */ 111 @NonNull requireToolbar(@onNull Activity activity)112 public static ToolbarController requireToolbar(@NonNull Activity activity) { 113 ToolbarController result = getToolbar(activity); 114 if (result == null) { 115 throw new IllegalArgumentException("Activity " + activity 116 + " does not have a CarUi Toolbar!" 117 + " Are you using Theme.CarUi.WithToolbar?"); 118 } 119 120 return result; 121 } 122 123 /** 124 * Registering a listener to receive the InsetsChanged updates instead of the Activity. 125 */ replaceInsetsChangedListenerWith(Activity activity, InsetsChangedListener listener)126 public static void replaceInsetsChangedListenerWith(Activity activity, 127 InsetsChangedListener listener) { 128 BaseLayoutController controller = getBaseLayoutController(activity); 129 if (controller != null) { 130 controller.replaceInsetsChangedListenerWith(listener); 131 } 132 } 133 134 /** 135 * Gets the current {@link Insets} of the given {@link Activity}. Only applies to Activities 136 * using the base layout, ie have the theme attribute "carUiBaseLayout" set to true. 137 * 138 * <p>Note that you likely don't want to use this without also using 139 * {@link com.android.car.ui.baselayout.InsetsChangedListener}, as without it the Insets 140 * will automatically be applied to your Activity's content view. 141 */ 142 @Nullable getInsets(@ullable Activity activity)143 public static Insets getInsets(@Nullable Activity activity) { 144 BaseLayoutController controller = getBaseLayoutController(activity); 145 if (controller != null) { 146 return controller.getInsets(); 147 } 148 return null; 149 } 150 151 /** 152 * Gets the current {@link Insets} of the given {@link Activity}. Only applies to Activities 153 * using the base layout, ie have the theme attribute "carUiBaseLayout" set to true. 154 * 155 * <p>Note that you likely don't want to use this without also using 156 * {@link com.android.car.ui.baselayout.InsetsChangedListener}, as without it the Insets 157 * will automatically be applied to your Activity's content view. 158 * 159 * @throws IllegalArgumentException When the activity is not using base layouts. 160 */ 161 @NonNull requireInsets(@onNull Activity activity)162 public static Insets requireInsets(@NonNull Activity activity) { 163 Insets result = getInsets(activity); 164 if (result == null) { 165 throw new IllegalArgumentException("Activity " + activity 166 + " does not have a base layout!" 167 + " Are you using Theme.CarUi.WithToolbar or Theme.CarUi.NoToolbar?"); 168 } 169 170 return result; 171 } 172 173 /** 174 * Most apps should not use this method, but instead rely on CarUi automatically 175 * installing the base layout into their activities. See {@link #requireToolbar(Activity)}. 176 * 177 * This method installs the base layout *around* the provided view. As a result, this view 178 * must have a parent ViewGroup. 179 * 180 * When using this method, you can't use the other activity-based methods. 181 * ({@link #requireToolbar(Activity)}, {@link #requireInsets(Activity)}, ect.) 182 * 183 * @see #installBaseLayoutAround(View, InsetsChangedListener, boolean, boolean) 184 * 185 * @param view The view to wrap inside a base layout. 186 * @param hasToolbar if there should be a toolbar in the base layout. 187 * @return The {@link ToolbarController}, which will be null if hasToolbar is false. 188 */ 189 @Nullable installBaseLayoutAround( View view, InsetsChangedListener insetsChangedListener, boolean hasToolbar)190 public static ToolbarController installBaseLayoutAround( 191 View view, 192 InsetsChangedListener insetsChangedListener, 193 boolean hasToolbar) { 194 return installBaseLayoutAround(view, insetsChangedListener, hasToolbar, true); 195 } 196 197 /** 198 * Most apps should not use this method, but instead rely on CarUi automatically 199 * installing the base layout into their activities. See {@link #requireToolbar(Activity)}. 200 * 201 * This method installs the base layout *around* the provided view. As a result, this view 202 * must have a parent ViewGroup. 203 * 204 * When using this method, you can't use the other activity-based methods. 205 * ({@link #requireToolbar(Activity)}, {@link #requireInsets(Activity)}, ect.) 206 * 207 * @param view The view to wrap inside a base layout. 208 * @param hasToolbar if there should be a toolbar in the base layout. 209 * @param fullscreen A hint specifying whether this view we're installing around takes up 210 * the whole screen or not. Used to know if putting decorations around 211 * the edges is appropriate. 212 * @return The {@link ToolbarController}, which will be null if hasToolbar is false. 213 */ 214 @Nullable installBaseLayoutAround( View view, InsetsChangedListener insetsChangedListener, boolean hasToolbar, boolean fullscreen)215 public static ToolbarController installBaseLayoutAround( 216 View view, 217 InsetsChangedListener insetsChangedListener, 218 boolean hasToolbar, 219 boolean fullscreen) { 220 return SharedLibraryFactorySingleton.get(view.getContext()) 221 .installBaseLayoutAround(view, insetsChangedListener, hasToolbar, fullscreen); 222 } 223 } 224