• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.car.settings.sound;
18 
19 import static com.android.car.settings.sound.VolumeItemParser.VolumeItem;
20 
21 import android.car.Car;
22 import android.car.CarNotConnectedException;
23 import android.car.drivingstate.CarUxRestrictions;
24 import android.car.media.CarAudioManager;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.ServiceConnection;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.util.SparseArray;
31 
32 import androidx.annotation.DrawableRes;
33 import androidx.annotation.StringRes;
34 import androidx.annotation.VisibleForTesting;
35 import androidx.annotation.XmlRes;
36 import androidx.preference.PreferenceGroup;
37 
38 import com.android.car.apps.common.util.Themes;
39 import com.android.car.settings.R;
40 import com.android.car.settings.common.FragmentController;
41 import com.android.car.settings.common.Logger;
42 import com.android.car.settings.common.PreferenceController;
43 import com.android.car.settings.common.SeekBarPreference;
44 
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * Business logic which parses car volume items into groups, creates a seek bar preference for each
50  * group, and interfaces with the ringtone manager and audio manager.
51  *
52  * @see VolumeSettingsRingtoneManager
53  * @see android.car.media.CarAudioManager
54  */
55 public class VolumeSettingsPreferenceController extends PreferenceController<PreferenceGroup> {
56     private static final Logger LOG = new Logger(VolumeSettingsPreferenceController.class);
57     private static final String VOLUME_GROUP_KEY = "volume_group_key";
58     private static final String VOLUME_USAGE_KEY = "volume_usage_key";
59 
60     private final SparseArray<VolumeItem> mVolumeItems;
61     private final List<SeekBarPreference> mVolumePreferences = new ArrayList<>();
62     private final VolumeSettingsRingtoneManager mRingtoneManager;
63 
64     private final ServiceConnection mServiceConnection = new ServiceConnection() {
65         @Override
66         public void onServiceConnected(ComponentName name, IBinder service) {
67             try {
68                 mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
69                 int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
70                 cleanUpVolumePreferences();
71                 // Populates volume slider items from volume groups to UI.
72                 for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
73                     VolumeItem volumeItem = getVolumeItemForUsages(
74                             mCarAudioManager.getUsagesForVolumeGroupId(groupId));
75                     SeekBarPreference volumePreference = createVolumeSeekBarPreference(
76                             groupId, volumeItem.getUsage(), volumeItem.getIcon(),
77                             volumeItem.getTitle());
78                     mVolumePreferences.add(volumePreference);
79                 }
80 
81                 refreshUi();
82             } catch (CarNotConnectedException e) {
83                 LOG.e("Car is not connected!", e);
84             }
85         }
86 
87         /** Cleanup audio related fields when car is disconnected. */
88         @Override
89         public void onServiceDisconnected(ComponentName name) {
90             cleanupAudioManager();
91         }
92     };
93 
94     private Car mCar;
95     private CarAudioManager mCarAudioManager;
96 
VolumeSettingsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)97     public VolumeSettingsPreferenceController(Context context, String preferenceKey,
98             FragmentController fragmentController,
99             CarUxRestrictions uxRestrictions) {
100         super(context, preferenceKey, fragmentController, uxRestrictions);
101         mCar = Car.createCar(getContext(), mServiceConnection);
102         mVolumeItems = VolumeItemParser.loadAudioUsageItems(context, carVolumeItemsXml());
103         mRingtoneManager = new VolumeSettingsRingtoneManager(getContext());
104     }
105 
106     @Override
getPreferenceType()107     protected Class<PreferenceGroup> getPreferenceType() {
108         return PreferenceGroup.class;
109     }
110 
111     /** Connect to car on create. */
112     @Override
onCreateInternal()113     protected void onCreateInternal() {
114         mCar.connect();
115     }
116 
117     /** Disconnect from car on destroy. */
118     @Override
onDestroyInternal()119     protected void onDestroyInternal() {
120         mCar.disconnect();
121     }
122 
123     @Override
updateState(PreferenceGroup preferenceGroup)124     protected void updateState(PreferenceGroup preferenceGroup) {
125         for (SeekBarPreference preference : mVolumePreferences) {
126             preferenceGroup.addPreference(preference);
127         }
128     }
129 
130     /**
131      * The resource which lists the car volume resources associated with the various usage enums.
132      */
133     @XmlRes
134     @VisibleForTesting
carVolumeItemsXml()135     int carVolumeItemsXml() {
136         return R.xml.car_volume_items;
137     }
138 
createVolumeSeekBarPreference( int volumeGroupId, int usage, @DrawableRes int iconResId, @StringRes int titleId)139     private SeekBarPreference createVolumeSeekBarPreference(
140             int volumeGroupId, int usage, @DrawableRes int iconResId,
141             @StringRes int titleId) {
142         SeekBarPreference preference = new SeekBarPreference(getContext());
143         preference.setTitle(getContext().getString(titleId));
144         preference.setIcon(getContext().getDrawable(iconResId));
145         preference.getIcon().setTintList(
146                 Themes.getAttrColorStateList(getContext(), R.attr.iconColor));
147         try {
148             preference.setValue(mCarAudioManager.getGroupVolume(volumeGroupId));
149             preference.setMin(mCarAudioManager.getGroupMinVolume(volumeGroupId));
150             preference.setMax(mCarAudioManager.getGroupMaxVolume(volumeGroupId));
151         } catch (CarNotConnectedException e) {
152             LOG.e("Car is not connected!", e);
153         }
154         preference.setContinuousUpdate(true);
155         preference.setShowSeekBarValue(false);
156         Bundle bundle = preference.getExtras();
157         bundle.putInt(VOLUME_GROUP_KEY, volumeGroupId);
158         bundle.putInt(VOLUME_USAGE_KEY, usage);
159         preference.setOnPreferenceChangeListener((pref, newValue) -> {
160             int prefGroup = pref.getExtras().getInt(VOLUME_GROUP_KEY);
161             int prefUsage = pref.getExtras().getInt(VOLUME_USAGE_KEY);
162             int newVolume = (Integer) newValue;
163             setGroupVolume(prefGroup, newVolume);
164             mRingtoneManager.playAudioFeedback(prefGroup, prefUsage);
165             return true;
166         });
167         return preference;
168     }
169 
setGroupVolume(int volumeGroupId, int newVolume)170     private void setGroupVolume(int volumeGroupId, int newVolume) {
171         try {
172             mCarAudioManager.setGroupVolume(volumeGroupId, newVolume, /* flags= */ 0);
173         } catch (CarNotConnectedException e) {
174             LOG.w("Ignoring volume change event because the car isn't connected", e);
175         }
176     }
177 
cleanupAudioManager()178     private void cleanupAudioManager() {
179         cleanUpVolumePreferences();
180         mCarAudioManager = null;
181     }
182 
cleanUpVolumePreferences()183     private void cleanUpVolumePreferences() {
184         mRingtoneManager.stopCurrentRingtone();
185         mVolumePreferences.clear();
186     }
187 
getVolumeItemForUsages(int[] usages)188     private VolumeItem getVolumeItemForUsages(int[] usages) {
189         int rank = Integer.MAX_VALUE;
190         VolumeItem result = null;
191         for (int usage : usages) {
192             VolumeItem volumeItem = mVolumeItems.get(usage);
193             if (volumeItem.getRank() < rank) {
194                 rank = volumeItem.getRank();
195                 result = volumeItem;
196             }
197         }
198         return result;
199     }
200 }
201