• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.settings.sound;
17 
18 import android.app.Activity;
19 import android.car.Car;
20 import android.car.CarNotConnectedException;
21 import android.car.media.CarAudioManager;
22 import android.annotation.DrawableRes;
23 import android.annotation.StringRes;
24 import android.content.ComponentName;
25 import android.content.ServiceConnection;
26 import android.media.AudioAttributes;
27 import android.media.IVolumeController;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import com.android.car.settings.common.BaseFragment;
36 import com.android.car.settings.common.TypedPagedListAdapter;
37 import com.android.car.settings.R;
38 import com.android.car.view.PagedListView;
39 
40 import java.util.ArrayList;
41 import java.util.HashSet;
42 
43 /**
44  * Activity hosts sound related settings.
45  */
46 public class SoundSettingsFragment extends BaseFragment {
47     private static final String TAG = "SoundSettingsFragment";
48     private Car mCar;
49     private CarAudioManager mCarAudioManager;
50     private PagedListView mListView;
51     private TypedPagedListAdapter mPagedListAdapter;
52 
53     private final ArrayList<VolumeLineItem> mVolumeLineItems = new ArrayList<>();
54     private final VolumeCallback
55             mVolumeCallback = new VolumeCallback();
56 
57     private final Handler handler = new Handler(Looper.getMainLooper());
58     private final HashSet<StreamItem> mUniqueStreamItems = new HashSet<>();
59 
60     private final ServiceConnection mServiceConnection = new ServiceConnection() {
61         @Override
62         public void onServiceConnected(ComponentName name, IBinder service) {
63             try {
64                 mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
65                 mCarAudioManager.setVolumeController(mVolumeCallback);
66 
67                 mUniqueStreamItems.add(new StreamItem(
68                         mCarAudioManager.getAudioAttributesForCarUsage(
69                                 mCarAudioManager.CAR_AUDIO_USAGE_MUSIC)
70                                 .getVolumeControlStream(),
71                         R.string.media_volume_title,
72                         com.android.internal.R.drawable.ic_audio_media));
73                 mUniqueStreamItems.add(new StreamItem(
74                         mCarAudioManager.getAudioAttributesForCarUsage(
75                                 mCarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND)
76                                 .getVolumeControlStream(),
77                         R.string.ring_volume_title,
78                         com.android.internal.R.drawable.ic_audio_ring_notif));
79                 mUniqueStreamItems.add(new StreamItem(
80                         mCarAudioManager.getAudioAttributesForCarUsage(
81                                 mCarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE)
82                                 .getVolumeControlStream(),
83                         R.string.navi_volume_title,
84                         R.drawable.ic_audio_navi));
85             } catch (CarNotConnectedException e) {
86                 Log.e(TAG, "Car is not connected!", e);
87                 return;
88             }
89 
90             for (StreamItem streamItem : mUniqueStreamItems) {
91                 mVolumeLineItems.add(new VolumeLineItem(
92                         getContext(),
93                         mCarAudioManager,
94                         streamItem.volumeStream,
95                         streamItem.nameStringId,
96                         streamItem.iconId));
97             }
98             // if list is already initiated, update it's content.
99             if (mPagedListAdapter != null) {
100                 mPagedListAdapter.updateList(new ArrayList<>(mVolumeLineItems));
101             }
102         }
103 
104         @Override
105         public void onServiceDisconnected(ComponentName name) {
106             try {
107                 mCarAudioManager.setVolumeController(null);
108             } catch (CarNotConnectedException e) {
109             Log.e(TAG, "Car is not connected!", e);
110             return;
111             }
112             mCarAudioManager = null;
113         }
114     };
115 
getInstance()116     public static SoundSettingsFragment getInstance() {
117         SoundSettingsFragment soundSettingsFragment = new SoundSettingsFragment();
118         Bundle bundle = BaseFragment.getBundle();
119         bundle.putInt(EXTRA_TITLE_ID, R.string.sound_settings);
120         bundle.putInt(EXTRA_LAYOUT, R.layout.list);
121         bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar);
122         soundSettingsFragment.setArguments(bundle);
123         return soundSettingsFragment;
124     }
125 
126     @Override
onCreate(Bundle savedInstanceState)127     public void onCreate(Bundle savedInstanceState) {
128         super.onCreate(savedInstanceState);
129         mCar = Car.createCar(getContext(), mServiceConnection);
130     }
131 
132     @Override
onActivityCreated(Bundle savedInstanceState)133     public void onActivityCreated(Bundle savedInstanceState) {
134         super.onActivityCreated(savedInstanceState);
135         mListView = (PagedListView) getView().findViewById(R.id.list);
136         mListView.setDarkMode();
137         mPagedListAdapter = new TypedPagedListAdapter(getContext());
138         mListView.setAdapter(mPagedListAdapter);
139         if (!mVolumeLineItems.isEmpty()) {
140             mPagedListAdapter.updateList(new ArrayList<>(mVolumeLineItems));
141         }
142     }
143 
144     @Override
onStart()145     public void onStart() {
146         super.onStart();
147         mCar.connect();
148     }
149 
150     @Override
onStop()151     public void onStop() {
152         super.onStop();
153         for (VolumeLineItem item : mVolumeLineItems) {
154             item.stop();
155         }
156         mVolumeLineItems.clear();
157         mCar.disconnect();
158     }
159 
160     /**
161      * The interface has a terrible name, it is actually a callback, so here name it accordingly.
162      */
163     private final class VolumeCallback extends IVolumeController.Stub {
164         @Override
displaySafeVolumeWarning(int flags)165         public void displaySafeVolumeWarning(int flags) throws RemoteException {
166         }
167 
168         @Override
volumeChanged(int streamType, int flags)169         public void volumeChanged(int streamType, int flags) throws RemoteException {
170             Activity activity = getActivity();
171             if (activity == null) {
172                 Log.w(TAG, "no activity attached.");
173                 return;
174             }
175 
176             for (VolumeLineItem item : mVolumeLineItems) {
177                 if (streamType == item.getStreamType()) {
178                     handler.post(() -> mPagedListAdapter.notifyDataSetChanged());
179                     return;
180                 }
181             }
182 
183         }
184 
185         // this is not mute of this stream
186         @Override
masterMuteChanged(int flags)187         public void masterMuteChanged(int flags) throws RemoteException {
188         }
189 
190         @Override
setLayoutDirection(int layoutDirection)191         public void setLayoutDirection(int layoutDirection) throws RemoteException {
192         }
193 
194         @Override
dismiss()195         public void dismiss() throws RemoteException {
196         }
197 
198         @Override
setA11yMode(int mode)199         public void setA11yMode(int mode) {
200         }
201     }
202 
203     /**
204      * Wraps information needed to render an audio stream, keyed by volumeControlStream.
205      */
206     private static final class StreamItem {
207         final int volumeStream;
208 
209         @StringRes
210         final int nameStringId;
211 
212         @DrawableRes
213         final int iconId;
214 
StreamItem( int volumeStream, @StringRes int nameStringId, @DrawableRes int iconId)215         StreamItem(
216                 int volumeStream,
217                 @StringRes int nameStringId,
218                 @DrawableRes int iconId) {
219             this.volumeStream = volumeStream;
220             this.nameStringId = nameStringId;
221             this.iconId = iconId;
222         }
223 
224         @Override
hashCode()225         public int hashCode() {
226             return volumeStream;
227         }
228 
229         @Override
equals(Object o)230         public boolean equals(Object o) {
231             if (this == o) {
232                 return true;
233             }
234             if (!(o instanceof  StreamItem)) {
235                 return false;
236             }
237             return volumeStream == ((StreamItem) o).volumeStream;
238         }
239     }
240 }
241