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 17 package com.android.car.settings.sound; 18 19 import android.car.Car; 20 import android.car.CarNotConnectedException; 21 import android.car.media.CarAudioManager; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.ServiceConnection; 25 import android.media.AudioManager; 26 import android.media.IVolumeController; 27 import android.media.Ringtone; 28 import android.media.RingtoneManager; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.RemoteException; 34 import android.provider.Settings; 35 import android.util.Log; 36 import android.view.View; 37 import android.widget.ImageView; 38 import android.widget.SeekBar; 39 import android.widget.SeekBar.OnSeekBarChangeListener; 40 import android.widget.TextView; 41 42 import com.android.car.settings.R; 43 44 /** 45 * Contains logic about volume controller UI. 46 */ 47 public class VolumeControllerPresenter implements OnSeekBarChangeListener { 48 49 private static final String TAG = "SeekBarVolumizer"; 50 private static final int AUDIO_FEEDBACK_DELAY_MS = 1500; 51 52 private final Handler mHandler = new Handler(Looper.getMainLooper()); 53 private final SeekBar mSeekBar; 54 private final int mStreamType; 55 private final Ringtone mRingtone; 56 private final VolumnCallback mVolumeCallback = new VolumnCallback(); 57 58 private CarAudioManager mCarAudioManager; 59 onServiceConnected(Car car)60 public void onServiceConnected(Car car) { 61 try { 62 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 63 mCarAudioManager.setVolumeController(mVolumeCallback); 64 mSeekBar.setMax(mCarAudioManager.getStreamMaxVolume(mStreamType)); 65 mSeekBar.setProgress(mCarAudioManager.getStreamVolume(mStreamType)); 66 mSeekBar.setOnSeekBarChangeListener(VolumeControllerPresenter.this); 67 } catch (CarNotConnectedException e) { 68 Log.e(TAG, "Car is not connected!", e); 69 } 70 } 71 onServiceDisconnected()72 public void onServiceDisconnected() { 73 mSeekBar.setOnSeekBarChangeListener(null); 74 mCarAudioManager = null; 75 } 76 VolumeControllerPresenter(Context context, View volumeControllerView, int streamType, Uri sampleUri, int titleStringResId, int iconResId)77 public VolumeControllerPresenter(Context context, View volumeControllerView, 78 int streamType, Uri sampleUri, int titleStringResId, int iconResId) { 79 mSeekBar = (SeekBar) volumeControllerView.findViewById(R.id.seekbar); 80 mStreamType = streamType; 81 Uri ringtoneUri; 82 83 if (sampleUri == null) { 84 switch (mStreamType) { 85 case AudioManager.STREAM_RING: 86 ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI; 87 break; 88 case AudioManager.STREAM_NOTIFICATION: 89 ringtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI; 90 break; 91 default: 92 ringtoneUri = Settings.System.DEFAULT_ALARM_ALERT_URI; 93 } 94 } else { 95 ringtoneUri = sampleUri; 96 } 97 mRingtone = RingtoneManager.getRingtone(context, ringtoneUri); 98 if (mRingtone != null) { 99 mRingtone.setStreamType(mStreamType); 100 } 101 ((ImageView) volumeControllerView.findViewById(R.id.icon)).setImageResource(iconResId); 102 ((TextView) volumeControllerView.findViewById(R.id.stream_name)).setText(titleStringResId); 103 } 104 stop()105 public void stop() { 106 mHandler.removeCallbacksAndMessages(null); 107 mRingtone.stop(); 108 } 109 110 @Override onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)111 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 112 try { 113 if (mCarAudioManager == null) { 114 Log.w(TAG, "CarAudiomanager not available, Car is not connected!"); 115 return; 116 } 117 mCarAudioManager.setStreamVolume(mStreamType, progress, AudioManager.FLAG_PLAY_SOUND); 118 playAudioFeedback(); 119 } catch (CarNotConnectedException e) { 120 Log.e(TAG, "Car is not connected!", e); 121 } 122 } 123 124 @Override onStartTrackingTouch(SeekBar seekBar)125 public void onStartTrackingTouch(SeekBar seekBar) { 126 127 } 128 129 @Override onStopTrackingTouch(SeekBar seekBar)130 public void onStopTrackingTouch(SeekBar seekBar) { 131 playAudioFeedback(); 132 } 133 playAudioFeedback()134 private void playAudioFeedback() { 135 mHandler.removeCallbacksAndMessages(null); 136 mRingtone.play(); 137 mHandler.postDelayed(() -> { 138 if (mRingtone.isPlaying()) { 139 mRingtone.stop(); 140 } 141 }, AUDIO_FEEDBACK_DELAY_MS); 142 } 143 144 /** 145 * The interface has a terrible name, it is actually a callback, so here name it accordingly. 146 */ 147 private final class VolumnCallback extends IVolumeController.Stub { 148 149 private final String TAG = VolumeControllerPresenter.TAG + ".cb"; 150 151 @Override displaySafeVolumeWarning(int flags)152 public void displaySafeVolumeWarning(int flags) throws RemoteException { 153 } 154 155 @Override volumeChanged(int streamType, int flags)156 public void volumeChanged(int streamType, int flags) throws RemoteException { 157 if (streamType != mStreamType) { 158 return; 159 } 160 try { 161 if (mCarAudioManager == null) { 162 Log.w(TAG, "CarAudiomanager not available, Car is not connected!"); 163 return; 164 } 165 int volume = mCarAudioManager.getStreamVolume(mStreamType); 166 if (mSeekBar.getProgress() == volume) { 167 return; 168 } 169 mSeekBar.setProgress(volume); 170 } catch (CarNotConnectedException e) { 171 Log.e(TAG, "Car is not connected!", e); 172 } 173 } 174 175 // this is not mute of this stream 176 @Override masterMuteChanged(int flags)177 public void masterMuteChanged(int flags) throws RemoteException { 178 } 179 180 @Override setLayoutDirection(int layoutDirection)181 public void setLayoutDirection(int layoutDirection) throws RemoteException { 182 } 183 184 @Override dismiss()185 public void dismiss() throws RemoteException { 186 } 187 188 @Override setA11yMode(int mode)189 public void setA11yMode(int mode) { 190 } 191 } 192 } 193