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