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 android.app.Activity;
19 
20 import androidx.annotation.NonNull;
21 import androidx.annotation.Nullable;
22 
23 import com.android.car.ui.baselayout.Insets;
24 import com.android.car.ui.baselayout.InsetsChangedListener;
25 import com.android.car.ui.toolbar.ToolbarController;
26 
27 import java.lang.reflect.Method;
28 
29 /**
30  * Public interface for general CarUi static functions.
31  */
32 public class CarUi {
33 
34     /** Prevent instantiating this class */
CarUi()35     private CarUi() {}
36 
37     /**
38      * Gets the {@link ToolbarController} for an activity. Requires that the Activity uses
39      * Theme.CarUi.WithToolbar, or otherwise sets carUiBaseLayout and carUiToolbar to true.
40      *
41      * See also: {@link #requireToolbar(Activity)}
42      */
43     @Nullable
getToolbar(Activity activity)44     public static ToolbarController getToolbar(Activity activity) {
45         BaseLayoutController controller = getBaseLayoutController(activity);
46         if (controller != null) {
47             return controller.getToolbarController();
48         }
49         return null;
50     }
51 
52     /**
53      * Gets the {@link ToolbarController} for an activity. Requires that the Activity uses
54      * Theme.CarUi.WithToolbar, or otherwise sets carUiBaseLayout and carUiToolbar to true.
55      *
56      * <p>See also: {@link #getToolbar(Activity)}
57      *
58      * @throws IllegalArgumentException When the CarUi Toolbar cannot be found.
59      */
60     @NonNull
requireToolbar(Activity activity)61     public static ToolbarController requireToolbar(Activity activity) {
62         ToolbarController result = getToolbar(activity);
63         if (result == null) {
64             throw new IllegalArgumentException("Activity " + activity
65                     + " does not have a CarUi Toolbar!"
66                     + " Are you using Theme.CarUi.WithToolbar?");
67         }
68 
69         return result;
70     }
71 
72     /**
73      * Registering a listener to receive the InsetsChanged updates instead of the Activity.
74      */
replaceInsetsChangedListenerWith(Activity activity, InsetsChangedListener listener)75     public static void replaceInsetsChangedListenerWith(Activity activity,
76             InsetsChangedListener listener) {
77         BaseLayoutController controller = getBaseLayoutController(activity);
78         if (controller != null) {
79             controller.replaceInsetsChangedListenerWith(listener);
80         }
81     }
82 
83     /**
84      * Gets the current {@link Insets} of the given {@link Activity}. Only applies to Activities
85      * using the base layout, ie have the theme attribute "carUiBaseLayout" set to true.
86      *
87      * <p>Note that you likely don't want to use this without also using
88      * {@link com.android.car.ui.baselayout.InsetsChangedListener}, as without it the Insets
89      * will automatically be applied to your Activity's content view.
90      */
91     @Nullable
getInsets(Activity activity)92     public static Insets getInsets(Activity activity) {
93         BaseLayoutController controller = getBaseLayoutController(activity);
94         if (controller != null) {
95             return controller.getInsets();
96         }
97         return null;
98     }
99 
100     /**
101      * Gets the current {@link Insets} of the given {@link Activity}. Only applies to Activities
102      * using the base layout, ie have the theme attribute "carUiBaseLayout" set to true.
103      *
104      * <p>Note that you likely don't want to use this without also using
105      * {@link com.android.car.ui.baselayout.InsetsChangedListener}, as without it the Insets
106      * will automatically be applied to your Activity's content view.
107      *
108      * @throws IllegalArgumentException When the activity is not using base layouts.
109      */
110     @NonNull
requireInsets(Activity activity)111     public static Insets requireInsets(Activity activity) {
112         Insets result = getInsets(activity);
113         if (result == null) {
114             throw new IllegalArgumentException("Activity " + activity
115                     + " does not have a base layout!"
116                     + " Are you using Theme.CarUi.WithToolbar or Theme.CarUi.NoToolbar?");
117         }
118 
119         return result;
120     }
121 
getBaseLayoutController(Activity activity)122     /* package */ static BaseLayoutController getBaseLayoutController(Activity activity) {
123         if (activity.getClassLoader().equals(CarUi.class.getClassLoader())) {
124             return BaseLayoutController.getBaseLayout(activity);
125         } else {
126             // Note: (b/156532465)
127             // The usage of the alternate classloader is to accommodate GMSCore.
128             // Some activities are loaded dynamically from external modules.
129             try {
130                 Class baseLayoutControllerClass = activity.getClassLoader()
131                         .loadClass(BaseLayoutController.class.getName());
132                 Method method = baseLayoutControllerClass
133                         .getDeclaredMethod("getBaseLayout", Activity.class);
134                 return (BaseLayoutController) method.invoke(null, activity);
135             } catch (ReflectiveOperationException e) {
136                 throw new RuntimeException(e);
137             }
138         }
139     }
140 }
141