1 /* 2 * Copyright (C) 2022 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.drivemode; 18 19 import android.content.Context; 20 import android.content.om.OverlayInfo; 21 import android.content.om.OverlayManager; 22 import android.os.UserHandle; 23 import android.text.TextUtils; 24 import android.util.Log; 25 26 import androidx.annotation.NonNull; 27 28 import com.android.systemui.dagger.SysUISingleton; 29 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 34 import javax.inject.Inject; 35 36 /** 37 * A class that activates and deactivates RROs based on the selected drive mode. 38 */ 39 @SysUISingleton 40 public class DriveModeThemeSwitcher implements DriveModeManager.Callback { 41 42 private static final boolean DEBUG = false; 43 private static final String TAG = "DriveModeThemeSwitcher"; 44 private static final String RRO_TARGET_PACKAGE = "android"; 45 private static final String RRO_PACKAGE = "drivemode.modes"; 46 47 private final OverlayManager mOverlayManager; 48 private final Map<String, String> mDriveModeOverlays; 49 50 @Inject DriveModeThemeSwitcher(Context context, DriveModeManager driveModeManager)51 DriveModeThemeSwitcher(Context context, DriveModeManager driveModeManager) { 52 mOverlayManager = context.getSystemService(OverlayManager.class); 53 if (mOverlayManager != null) { 54 mDriveModeOverlays = createDriveModeToRROPackageMap(); 55 driveModeManager.addCallback(this); 56 } else { 57 mDriveModeOverlays = new HashMap<>(); 58 if (DEBUG) Log.e(TAG, "No overlay manager"); 59 } 60 } 61 62 @Override onDriveModeChanged(@onNull String newDriveMode)63 public void onDriveModeChanged(@NonNull String newDriveMode) { 64 String overlayName = mDriveModeOverlays.getOrDefault(newDriveMode, null); 65 if (isOverlayActive(overlayName)) { 66 return; 67 } 68 disableActiveOverlays(); 69 enableOverlay(mDriveModeOverlays.get(newDriveMode)); 70 } 71 isOverlayActive(String packageName)72 private boolean isOverlayActive(String packageName) { 73 if (mOverlayManager == null || TextUtils.isEmpty(packageName)) { 74 return false; 75 } 76 77 OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(packageName, UserHandle.CURRENT); 78 return overlayInfo != null && overlayInfo.isEnabled(); 79 } 80 enableOverlay(String packageName)81 private void enableOverlay(String packageName) { 82 if (mOverlayManager == null || packageName == null) { 83 return; 84 } 85 mOverlayManager.setEnabled(packageName, true, UserHandle.CURRENT); 86 } 87 disableActiveOverlays()88 private void disableActiveOverlays() { 89 if (mOverlayManager == null) { 90 return; 91 } 92 93 mDriveModeOverlays.forEach((overlayName, overlayPackage) -> { 94 if (mOverlayManager.getOverlayInfo(overlayPackage, UserHandle.CURRENT).isEnabled()) { 95 mOverlayManager.setEnabled(overlayPackage, false, UserHandle.CURRENT); 96 } 97 }); 98 } 99 100 /* 101 * Method that initializes a map of <DriveMode, RRO Package> pairs. For convenience, the drive 102 * mode is extracted directly from the RRO package in this iteration. In a real world scenario 103 * there would be a separate mapping of the DriveMode to its RRO package 104 * (e.g. an array in Resources). 105 */ createDriveModeToRROPackageMap()106 private HashMap<String, String> createDriveModeToRROPackageMap() { 107 List<OverlayInfo> rroList = mOverlayManager.getOverlayInfosForTarget( 108 RRO_TARGET_PACKAGE, 109 UserHandle.CURRENT 110 ); 111 112 HashMap<String, String> driveModeOverlays = new HashMap<>(); 113 rroList.forEach(rro -> { 114 if (rro.packageName.contains(RRO_PACKAGE)) { 115 driveModeOverlays.put(extractRROName(rro.packageName), rro.packageName); 116 } 117 }); 118 return driveModeOverlays; 119 } 120 extractRROName(String overlayPackage)121 private String extractRROName(String overlayPackage) { 122 String name; 123 try { 124 String[] split = overlayPackage.split("\\."); 125 String rawName = split[split.length - 2]; 126 name = rawName.substring(0, 1).toUpperCase() + rawName.substring(1); 127 } catch (IndexOutOfBoundsException e) { 128 Log.e(TAG, "Error extracting the name from the RRO " + overlayPackage); 129 name = "Name not found"; 130 } catch (NullPointerException e) { 131 Log.e(TAG, "Error extracting the name from the RRO, overlayPackage is null."); 132 name = "Name not found"; 133 } 134 135 return name; 136 } 137 } 138