1 /* 2 * Copyright (C) 2015 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.google.android.car.kitchensink.volume; 17 18 import android.car.Car; 19 import android.car.media.CarAudioManager; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.ServiceConnection; 23 import android.media.AudioManager; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Message; 28 import android.util.Log; 29 import android.util.SparseIntArray; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.ListView; 34 import android.widget.SeekBar; 35 36 import androidx.annotation.Nullable; 37 import androidx.fragment.app.Fragment; 38 39 import com.google.android.car.kitchensink.R; 40 41 public class VolumeTestFragment extends Fragment { 42 private static final String TAG = "CarVolumeTest"; 43 private static final int MSG_VOLUME_CHANGED = 0; 44 private static final int MSG_REQUEST_FOCUS = 1; 45 private static final int MSG_FOCUS_CHANGED= 2; 46 47 private AudioManager mAudioManager; 48 private VolumeAdapter mAdapter; 49 50 private CarAudioManager mCarAudioManager; 51 private Car mCar; 52 53 private SeekBar mFader; 54 private SeekBar mBalance; 55 56 private final Handler mHandler = new VolumeHandler(); 57 58 private class VolumeHandler extends Handler { 59 private AudioFocusListener mFocusListener; 60 61 @Override handleMessage(Message msg)62 public void handleMessage(Message msg) { 63 switch (msg.what) { 64 case MSG_VOLUME_CHANGED: 65 initVolumeInfo(); 66 break; 67 case MSG_REQUEST_FOCUS: 68 int groupId = msg.arg1; 69 if (mFocusListener != null) { 70 mAudioManager.abandonAudioFocus(mFocusListener); 71 mVolumeInfos[mGroupIdIndexMap.get(groupId)].mHasFocus = false; 72 mAdapter.notifyDataSetChanged(); 73 } 74 75 mFocusListener = new AudioFocusListener(groupId); 76 mAudioManager.requestAudioFocus(mFocusListener, groupId, 77 AudioManager.AUDIOFOCUS_GAIN); 78 break; 79 case MSG_FOCUS_CHANGED: 80 int focusGroupId = msg.arg1; 81 mVolumeInfos[mGroupIdIndexMap.get(focusGroupId)].mHasFocus = true; 82 mAdapter.refreshVolumes(mVolumeInfos); 83 break; 84 85 } 86 } 87 } 88 89 private VolumeInfo[] mVolumeInfos = new VolumeInfo[0]; 90 private SparseIntArray mGroupIdIndexMap = new SparseIntArray(); 91 92 private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener { 93 private final int mGroupId; AudioFocusListener(int groupId)94 public AudioFocusListener(int groupId) { 95 mGroupId = groupId; 96 } 97 @Override onAudioFocusChange(int focusChange)98 public void onAudioFocusChange(int focusChange) { 99 if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { 100 mHandler.sendMessage(mHandler.obtainMessage(MSG_FOCUS_CHANGED, mGroupId, 0)); 101 } else { 102 Log.e(TAG, "Audio focus request failed"); 103 } 104 } 105 } 106 107 public static class VolumeInfo { 108 public int mGroupId; 109 public String mId; 110 public String mMax; 111 public String mCurrent; 112 public boolean mHasFocus; 113 } 114 115 private final ServiceConnection mCarConnectionCallback = 116 new ServiceConnection() { 117 @Override 118 public void onServiceConnected(ComponentName name, IBinder binder) { 119 Log.d(TAG, "Connected to Car Service"); 120 mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); 121 initVolumeInfo(); 122 } 123 124 @Override 125 public void onServiceDisconnected(ComponentName name) { 126 Log.d(TAG, "Disconnect from Car Service"); 127 } 128 }; 129 130 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)131 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 132 @Nullable Bundle savedInstanceState) { 133 View v = inflater.inflate(R.layout.volume_test, container, false); 134 135 ListView volumeListView = v.findViewById(R.id.volume_list); 136 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 137 138 mAdapter = new VolumeAdapter(getContext(), R.layout.volume_item, mVolumeInfos, this); 139 volumeListView.setAdapter(mAdapter); 140 141 v.findViewById(R.id.refresh).setOnClickListener((view) -> initVolumeInfo()); 142 143 final SeekBar.OnSeekBarChangeListener seekListener = new SeekBar.OnSeekBarChangeListener() { 144 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 145 final float percent = (progress - 100) / 100.0f; 146 if (seekBar.getId() == R.id.fade_bar) { 147 mCarAudioManager.setFadeTowardFront(percent); 148 } else { 149 mCarAudioManager.setBalanceTowardRight(percent); 150 } 151 } 152 153 public void onStartTrackingTouch(SeekBar seekBar) {} 154 155 public void onStopTrackingTouch(SeekBar seekBar) {} 156 }; 157 158 mFader = v.findViewById(R.id.fade_bar); 159 mFader.setOnSeekBarChangeListener(seekListener); 160 161 mBalance = v.findViewById(R.id.balance_bar); 162 mBalance.setOnSeekBarChangeListener(seekListener); 163 164 mCar = Car.createCar(getActivity(), mCarConnectionCallback); 165 mCar.connect(); 166 return v; 167 } 168 adjustVolumeByOne(int groupId, boolean up)169 public void adjustVolumeByOne(int groupId, boolean up) { 170 if (mCarAudioManager == null) { 171 Log.e(TAG, "CarAudioManager is null"); 172 return; 173 } 174 int current = mCarAudioManager.getGroupVolume(groupId); 175 int volume = current + (up ? 1 : -1); 176 mCarAudioManager.setGroupVolume(groupId, volume, 177 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND); 178 Log.d(TAG, "Set group " + groupId + " volume " + volume); 179 } 180 requestFocus(int groupId)181 public void requestFocus(int groupId) { 182 mHandler.sendMessage(mHandler.obtainMessage(MSG_REQUEST_FOCUS, groupId)); 183 } 184 initVolumeInfo()185 private void initVolumeInfo() { 186 int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); 187 mVolumeInfos = new VolumeInfo[volumeGroupCount + 1]; 188 mGroupIdIndexMap.clear(); 189 mVolumeInfos[0] = new VolumeInfo(); 190 mVolumeInfos[0].mId = "Group id"; 191 mVolumeInfos[0].mCurrent = "Current"; 192 mVolumeInfos[0].mMax = "Max"; 193 194 int i = 1; 195 for (int groupId = 0; groupId < volumeGroupCount; groupId++) { 196 mVolumeInfos[i] = new VolumeInfo(); 197 mVolumeInfos[i].mGroupId = groupId; 198 mGroupIdIndexMap.put(groupId, i); 199 mVolumeInfos[i].mId = String.valueOf(groupId); 200 201 202 int current = mCarAudioManager.getGroupVolume(groupId); 203 int max = mCarAudioManager.getGroupMaxVolume(groupId); 204 mVolumeInfos[i].mCurrent = String.valueOf(current); 205 mVolumeInfos[i].mMax = String.valueOf(max); 206 207 Log.d(TAG, groupId + " max: " + mVolumeInfos[i].mMax + " current: " 208 + mVolumeInfos[i].mCurrent); 209 i++; 210 } 211 mAdapter.refreshVolumes(mVolumeInfos); 212 } 213 214 @Override onDestroy()215 public void onDestroy() { 216 if (mCar != null) { 217 mCar.disconnect(); 218 } 219 super.onDestroy(); 220 } 221 } 222