1 /* 2 * Copyright (C) 2023 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.statusicon.ui; 18 19 import static android.car.media.CarAudioManager.INVALID_AUDIO_ZONE; 20 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE; 21 import static android.media.AudioAttributes.USAGE_MEDIA; 22 23 import android.car.Car; 24 import android.car.CarOccupantZoneManager; 25 import android.car.media.CarAudioManager; 26 import android.content.Context; 27 import android.content.res.Resources; 28 import android.graphics.drawable.Drawable; 29 30 import androidx.annotation.Nullable; 31 import androidx.annotation.VisibleForTesting; 32 33 import com.android.systemui.R; 34 import com.android.systemui.car.CarServiceProvider; 35 import com.android.systemui.car.statusicon.StatusIconView; 36 import com.android.systemui.car.statusicon.StatusIconViewController; 37 import com.android.systemui.car.systembar.element.CarSystemBarElementStateController; 38 import com.android.systemui.car.systembar.element.CarSystemBarElementStatusBarDisableController; 39 import com.android.systemui.dagger.qualifiers.Main; 40 import com.android.systemui.settings.UserTracker; 41 42 import dagger.assisted.Assisted; 43 import dagger.assisted.AssistedFactory; 44 import dagger.assisted.AssistedInject; 45 46 /** 47 * A controller for media volume status icon in the QC entry point area. 48 */ 49 public class MediaVolumeStatusIconController extends StatusIconViewController { 50 51 private final Context mContext; 52 private final Resources mResources; 53 private final UserTracker mUserTracker; 54 private final CarServiceProvider mCarServiceProvider; 55 56 private CarAudioManager mCarAudioManager; 57 private Drawable mMediaVolumeStatusIconDrawable; 58 private int mMaxMediaVolume; 59 private int mMinMediaVolume; 60 private int mGroupId; 61 private int mZoneId; 62 63 private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() { 64 @Override 65 public void onUserChanged(int newUser, Context userContext) { 66 updateStatus(mZoneId, mGroupId); 67 } 68 }; 69 70 private final CarServiceProvider.CarServiceOnConnectedListener mCarOnConnectedListener = 71 new CarServiceProvider.CarServiceOnConnectedListener() { 72 @Override 73 public void onConnected(Car car) { 74 CarOccupantZoneManager.OccupantZoneInfo occupantZoneInfo = null; 75 CarOccupantZoneManager carOccupantZoneManager = 76 (CarOccupantZoneManager) car.getCarManager( 77 Car.CAR_OCCUPANT_ZONE_SERVICE); 78 if (carOccupantZoneManager != null) { 79 occupantZoneInfo = carOccupantZoneManager.getMyOccupantZone(); 80 } 81 mZoneId = PRIMARY_AUDIO_ZONE; 82 if (occupantZoneInfo != null) { 83 int occupantAudioId = carOccupantZoneManager 84 .getAudioZoneIdForOccupant(occupantZoneInfo); 85 if (occupantAudioId != INVALID_AUDIO_ZONE) { 86 mZoneId = occupantAudioId; 87 } 88 } 89 90 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 91 92 if (mCarAudioManager != null) { 93 mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); 94 mGroupId = mCarAudioManager.getVolumeGroupIdForUsage(mZoneId, USAGE_MEDIA); 95 mMaxMediaVolume = mCarAudioManager.getGroupMaxVolume(mZoneId, mGroupId); 96 mMinMediaVolume = mCarAudioManager.getGroupMinVolume(mZoneId, mGroupId); 97 } 98 updateStatus(mZoneId, mGroupId); 99 } 100 }; 101 102 @AssistedInject MediaVolumeStatusIconController( @ssisted StatusIconView view, CarSystemBarElementStatusBarDisableController disableController, CarSystemBarElementStateController stateController, Context context, UserTracker userTracker, @Main Resources resources, CarServiceProvider carServiceProvider)103 MediaVolumeStatusIconController( 104 @Assisted StatusIconView view, 105 CarSystemBarElementStatusBarDisableController disableController, 106 CarSystemBarElementStateController stateController, 107 Context context, 108 UserTracker userTracker, 109 @Main Resources resources, 110 CarServiceProvider carServiceProvider) { 111 super(view, disableController, stateController); 112 mContext = context; 113 mResources = resources; 114 mUserTracker = userTracker; 115 mCarServiceProvider = carServiceProvider; 116 } 117 118 @AssistedFactory 119 public interface Factory extends 120 StatusIconViewController.Factory<MediaVolumeStatusIconController> { 121 } 122 123 @Override onViewAttached()124 protected void onViewAttached() { 125 super.onViewAttached(); 126 mUserTracker.addCallback(mUserTrackerCallback, mContext.getMainExecutor()); 127 mCarServiceProvider.addListener(mCarOnConnectedListener); 128 } 129 130 @Override onViewDetached()131 protected void onViewDetached() { 132 super.onViewDetached(); 133 mCarServiceProvider.removeListener(mCarOnConnectedListener); 134 mUserTracker.removeCallback(mUserTrackerCallback); 135 if (mCarAudioManager != null) { 136 mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback); 137 } 138 } 139 140 @Override updateStatus()141 protected void updateStatus() { 142 setIconDrawableToDisplay(mMediaVolumeStatusIconDrawable); 143 onStatusUpdated(); 144 } 145 updateStatus(int zoneId, int groupId)146 private void updateStatus(int zoneId, int groupId) { 147 if (mZoneId != zoneId || mGroupId != groupId) { 148 return; 149 } 150 if (mCarAudioManager != null) { 151 int currentMediaVolumeLevel = mCarAudioManager.getGroupVolume(mZoneId, mGroupId); 152 if (currentMediaVolumeLevel == mMinMediaVolume) { 153 mMediaVolumeStatusIconDrawable = mResources.getDrawable( 154 R.drawable.car_ic_media_volume_off, mContext.getTheme()); 155 } else if (currentMediaVolumeLevel <= (mMaxMediaVolume / 2)) { 156 mMediaVolumeStatusIconDrawable = mResources.getDrawable( 157 R.drawable.car_ic_media_volume_down, mContext.getTheme()); 158 } else { 159 mMediaVolumeStatusIconDrawable = mResources.getDrawable( 160 R.drawable.car_ic_media_volume_up, mContext.getTheme()); 161 } 162 updateStatus(); 163 } 164 } 165 166 private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = 167 new CarAudioManager.CarVolumeCallback() { 168 @Override 169 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 170 updateStatus(zoneId, groupId); 171 } 172 173 @Override 174 public void onMasterMuteChanged(int zoneId, int flags) { 175 // Mute is not being used yet 176 } 177 178 @Override 179 public void onGroupMuteChanged(int zoneId, int groupId, int flags) { 180 updateStatus(zoneId, groupId); 181 } 182 }; 183 184 /** 185 * Get the current media volume status icon in the QC entry point area. 186 */ 187 @VisibleForTesting 188 @Nullable getMediaVolumeStatusIconDrawable()189 Drawable getMediaVolumeStatusIconDrawable() { 190 return mMediaVolumeStatusIconDrawable; 191 } 192 } 193