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 17 package com.android.systemui.car.systembar; 18 19 import android.annotation.IdRes; 20 import android.content.Context; 21 import android.util.ArrayMap; 22 import android.util.Log; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 import androidx.annotation.LayoutRes; 27 28 import com.android.car.dockutil.Flags; 29 import com.android.car.ui.FocusParkingView; 30 import com.android.systemui.R; 31 import com.android.systemui.car.systembar.element.CarSystemBarElementController; 32 import com.android.systemui.car.systembar.element.CarSystemBarElementInitializer; 33 import com.android.systemui.dagger.SysUISingleton; 34 import com.android.systemui.flags.FeatureFlags; 35 import com.android.systemui.settings.UserTracker; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 40 import javax.inject.Inject; 41 42 /** A factory that creates and caches views for navigation bars. */ 43 @SysUISingleton 44 public class CarSystemBarViewFactory { 45 46 private static final String TAG = CarSystemBarViewFactory.class.getSimpleName(); 47 private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping(); 48 setupLayoutMapping()49 private static ArrayMap<Type, Integer> setupLayoutMapping() { 50 ArrayMap<Type, Integer> map = new ArrayMap<>(); 51 map.put(Type.TOP, R.layout.car_top_system_bar); 52 map.put(Type.TOP_WITH_DOCK, R.layout.car_top_system_bar_dock); 53 map.put(Type.TOP_UNPROVISIONED, R.layout.car_top_system_bar_unprovisioned); 54 map.put(Type.BOTTOM, R.layout.car_bottom_system_bar); 55 map.put(Type.BOTTOM_WITH_DOCK, R.layout.car_bottom_system_bar_dock); 56 map.put(Type.BOTTOM_UNPROVISIONED, R.layout.car_bottom_system_bar_unprovisioned); 57 map.put(Type.LEFT, R.layout.car_left_system_bar); 58 map.put(Type.LEFT_UNPROVISIONED, R.layout.car_left_system_bar_unprovisioned); 59 map.put(Type.RIGHT, R.layout.car_right_system_bar); 60 map.put(Type.RIGHT_UNPROVISIONED, R.layout.car_right_system_bar_unprovisioned); 61 return map; 62 } 63 64 private final Context mContext; 65 private final ArrayMap<Type, CarSystemBarView> mCachedViewMap = new ArrayMap<>( 66 Type.values().length); 67 private final ArrayMap<Type, ViewGroup> mCachedContainerMap = new ArrayMap<>(); 68 private final FeatureFlags mFeatureFlags; 69 private final UserTracker mUserTracker; 70 private final CarSystemBarElementInitializer mCarSystemBarElementInitializer; 71 private final List<CarSystemBarElementController> mCarSystemBarElementControllers = 72 new ArrayList<>(); 73 74 /** Type of navigation bar to be created. */ 75 private enum Type { 76 TOP, 77 TOP_WITH_DOCK, 78 TOP_UNPROVISIONED, 79 BOTTOM, 80 BOTTOM_WITH_DOCK, 81 BOTTOM_UNPROVISIONED, 82 LEFT, 83 LEFT_UNPROVISIONED, 84 RIGHT, 85 RIGHT_UNPROVISIONED 86 } 87 88 @Inject CarSystemBarViewFactory( Context context, FeatureFlags featureFlags, UserTracker userTracker, CarSystemBarElementInitializer elementInitializer )89 public CarSystemBarViewFactory( 90 Context context, 91 FeatureFlags featureFlags, 92 UserTracker userTracker, 93 CarSystemBarElementInitializer elementInitializer 94 ) { 95 mContext = context; 96 mFeatureFlags = featureFlags; 97 mUserTracker = userTracker; 98 mCarSystemBarElementInitializer = elementInitializer; 99 } 100 101 /** Gets the top window. */ getTopWindow()102 public ViewGroup getTopWindow() { 103 return getWindowCached(Type.TOP); 104 } 105 106 /** Gets the bottom window. */ getBottomWindow()107 public ViewGroup getBottomWindow() { 108 return getWindowCached(Type.BOTTOM); 109 } 110 111 /** Gets the left window. */ getLeftWindow()112 public ViewGroup getLeftWindow() { 113 return getWindowCached(Type.LEFT); 114 } 115 116 /** Gets the right window. */ getRightWindow()117 public ViewGroup getRightWindow() { 118 return getWindowCached(Type.RIGHT); 119 } 120 121 /** Gets the top bar. */ getTopBar(boolean isSetUp)122 public CarSystemBarView getTopBar(boolean isSetUp) { 123 if (Flags.dockFeature()) { 124 return getBar(isSetUp, Type.TOP_WITH_DOCK, Type.TOP_UNPROVISIONED); 125 } 126 return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED); 127 } 128 129 /** Gets the bottom bar. */ getBottomBar(boolean isSetUp)130 public CarSystemBarView getBottomBar(boolean isSetUp) { 131 if (Flags.dockFeature()) { 132 return getBar(isSetUp, Type.BOTTOM_WITH_DOCK, Type.BOTTOM_UNPROVISIONED); 133 } 134 return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED); 135 } 136 137 /** Gets the left bar. */ getLeftBar(boolean isSetUp)138 public CarSystemBarView getLeftBar(boolean isSetUp) { 139 return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED); 140 } 141 142 /** Gets the right bar. */ getRightBar(boolean isSetUp)143 public CarSystemBarView getRightBar(boolean isSetUp) { 144 return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED); 145 } 146 getWindowCached(Type type)147 private ViewGroup getWindowCached(Type type) { 148 if (mCachedContainerMap.containsKey(type)) { 149 return mCachedContainerMap.get(type); 150 } 151 152 ViewGroup window = (ViewGroup) View.inflate(mContext, 153 R.layout.navigation_bar_window, /* root= */ null); 154 window.setId(getWindowId(type)); 155 mCachedContainerMap.put(type, window); 156 return mCachedContainerMap.get(type); 157 } 158 159 @IdRes getWindowId(Type type)160 private int getWindowId(Type type) { 161 return switch (type) { 162 case TOP -> R.id.car_top_bar_window; 163 case BOTTOM -> R.id.car_bottom_bar_window; 164 case LEFT -> R.id.car_left_bar_window; 165 case RIGHT -> R.id.car_right_bar_window; 166 default -> throw new IllegalArgumentException("unknown system bar window type " + type); 167 }; 168 } 169 getBar(boolean isSetUp, Type provisioned, Type unprovisioned)170 private CarSystemBarView getBar(boolean isSetUp, Type provisioned, Type unprovisioned) { 171 CarSystemBarView view = getBarCached(isSetUp, provisioned, unprovisioned); 172 173 if (view == null) { 174 String name = isSetUp ? provisioned.name() : unprovisioned.name(); 175 Log.e(TAG, "CarStatusBar failed inflate for " + name); 176 throw new RuntimeException( 177 "Unable to build " + name + " nav bar due to missing layout"); 178 } 179 return view; 180 } 181 getBarCached(boolean isSetUp, Type provisioned, Type unprovisioned)182 private CarSystemBarView getBarCached(boolean isSetUp, Type provisioned, Type unprovisioned) { 183 Type type = isSetUp ? provisioned : unprovisioned; 184 if (mCachedViewMap.containsKey(type)) { 185 return mCachedViewMap.get(type); 186 } 187 188 Integer barLayoutInteger = sLayoutMap.get(type); 189 if (barLayoutInteger == null) { 190 return null; 191 } 192 @LayoutRes int barLayout = barLayoutInteger; 193 CarSystemBarView view = (CarSystemBarView) View.inflate(mContext, barLayout, 194 /* root= */ null); 195 196 view.setupHvacButton(); 197 view.setupSystemBarButtons(mUserTracker); 198 mCarSystemBarElementControllers.addAll( 199 mCarSystemBarElementInitializer.initializeCarSystemBarElements(view)); 200 201 // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here 202 // when the user navigates to another window. This is also used to prevent wrap-around. 203 view.addView(new FocusParkingView(mContext), 0); 204 205 mCachedViewMap.put(type, view); 206 return mCachedViewMap.get(type); 207 } 208 209 /** Resets the cached system bar views. */ resetSystemBarViewCache()210 protected void resetSystemBarViewCache() { 211 mCachedViewMap.clear(); 212 } 213 214 /** Resets the cached system bar windows and system bar views. */ resetSystemBarWindowCache()215 protected void resetSystemBarWindowCache() { 216 resetSystemBarViewCache(); 217 mCachedContainerMap.clear(); 218 } 219 } 220