1 /* 2 * Copyright (C) 2020 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.google.android.car.kitchensink.volume; 18 19 import android.car.media.CarAudioManager; 20 import android.media.AudioManager; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.os.Message; 24 import android.util.Log; 25 import android.util.SparseIntArray; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ListView; 30 31 import androidx.fragment.app.Fragment; 32 33 import com.google.android.car.kitchensink.R; 34 import com.google.android.car.kitchensink.volume.VolumeTestFragment.CarAudioZoneVolumeInfo; 35 36 public final class CarAudioZoneVolumeFragment extends Fragment { 37 private static final String TAG = "CarVolumeTest." 38 + CarAudioZoneVolumeFragment.class.getSimpleName(); 39 private static final boolean DEBUG = true; 40 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 final int mZoneId; 46 private final CarAudioManager mCarAudioManager; 47 private final AudioManager mAudioManager; 48 private CarAudioZoneVolumeInfo[] mVolumeInfos = 49 new CarAudioZoneVolumeInfo[0]; 50 private final Handler mHandler = new VolumeHandler(); 51 52 private CarAudioZoneVolumeAdapter mCarAudioZoneVolumeAdapter; 53 private final SparseIntArray mGroupIdIndexMap = new SparseIntArray(); 54 sendChangeMessage()55 public void sendChangeMessage() { 56 mHandler.sendMessage(mHandler.obtainMessage(MSG_VOLUME_CHANGED)); 57 } 58 59 private class VolumeHandler extends Handler { 60 private AudioFocusListener mFocusListener; 61 62 @Override handleMessage(Message msg)63 public void handleMessage(Message msg) { 64 if (DEBUG) { 65 Log.d(TAG, "zone " + mZoneId + " handleMessage : " + getMessageName(msg)); 66 } 67 switch (msg.what) { 68 case MSG_VOLUME_CHANGED: 69 initVolumeInfo(); 70 break; 71 case MSG_REQUEST_FOCUS: 72 int groupId = msg.arg1; 73 if (mFocusListener != null) { 74 mAudioManager.abandonAudioFocus(mFocusListener); 75 mVolumeInfos[mGroupIdIndexMap.get(groupId)].mHasFocus = false; 76 mCarAudioZoneVolumeAdapter.notifyDataSetChanged(); 77 } 78 79 mFocusListener = new AudioFocusListener(groupId); 80 mAudioManager.requestAudioFocus(mFocusListener, groupId, 81 AudioManager.AUDIOFOCUS_GAIN); 82 break; 83 case MSG_FOCUS_CHANGED: 84 int focusGroupId = msg.arg1; 85 mVolumeInfos[mGroupIdIndexMap.get(focusGroupId)].mHasFocus = true; 86 mCarAudioZoneVolumeAdapter.refreshVolumes(mVolumeInfos); 87 break; 88 default : 89 Log.wtf(TAG,"VolumeHandler handleMessage called with unknown message" 90 + msg.what); 91 92 } 93 } 94 } 95 CarAudioZoneVolumeFragment(int zoneId, CarAudioManager carAudioManager, AudioManager audioManager)96 public CarAudioZoneVolumeFragment(int zoneId, CarAudioManager carAudioManager, 97 AudioManager audioManager) { 98 mZoneId = zoneId; 99 mCarAudioManager = carAudioManager; 100 mAudioManager = audioManager; 101 } 102 103 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)104 public View onCreateView(LayoutInflater inflater, ViewGroup container, 105 Bundle savedInstanceState) { 106 if (DEBUG) { 107 Log.d(TAG, "onCreateView " + mZoneId); 108 } 109 View v = inflater.inflate(R.layout.zone_volume_tab, container, false); 110 ListView volumeListView = v.findViewById(R.id.volume_list); 111 mCarAudioZoneVolumeAdapter = 112 new CarAudioZoneVolumeAdapter(getContext(), R.layout.volume_item, mVolumeInfos, 113 this); 114 initVolumeInfo(); 115 volumeListView.setAdapter(mCarAudioZoneVolumeAdapter); 116 return v; 117 } 118 initVolumeInfo()119 void initVolumeInfo() { 120 int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(mZoneId); 121 mVolumeInfos = new CarAudioZoneVolumeInfo[volumeGroupCount + 1]; 122 mGroupIdIndexMap.clear(); 123 CarAudioZoneVolumeInfo titlesInfo = new CarAudioZoneVolumeInfo(); 124 titlesInfo.mId = "Group id"; 125 titlesInfo.mCurrent = "Current"; 126 titlesInfo.mMax = "Max"; 127 mVolumeInfos[0] = titlesInfo; 128 129 int i = 1; 130 for (int groupId = 0; groupId < volumeGroupCount; groupId++) { 131 CarAudioZoneVolumeInfo volumeInfo = new CarAudioZoneVolumeInfo(); 132 mGroupIdIndexMap.put(groupId, i); 133 volumeInfo.mGroupId = groupId; 134 volumeInfo.mId = String.valueOf(groupId); 135 int current = mCarAudioManager.getGroupVolume(mZoneId, groupId); 136 int max = mCarAudioManager.getGroupMaxVolume(mZoneId, groupId); 137 volumeInfo.mCurrent = String.valueOf(current); 138 volumeInfo.mMax = String.valueOf(max); 139 140 mVolumeInfos[i] = volumeInfo; 141 if (DEBUG) 142 { 143 Log.d(TAG, groupId + " max: " + volumeInfo.mMax + " current: " 144 + volumeInfo.mCurrent); 145 } 146 i++; 147 } 148 mCarAudioZoneVolumeAdapter.refreshVolumes(mVolumeInfos); 149 } 150 adjustVolumeByOne(int groupId, boolean up)151 public void adjustVolumeByOne(int groupId, boolean up) { 152 if (mCarAudioManager == null) { 153 Log.e(TAG, "CarAudioManager is null"); 154 return; 155 } 156 int current = mCarAudioManager.getGroupVolume(mZoneId, groupId); 157 int volume = current + (up ? 1 : -1); 158 mCarAudioManager.setGroupVolume(mZoneId, groupId, volume, 159 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND); 160 if (DEBUG) { 161 Log.d(TAG, "Set group " + groupId + " volume " + volume + " in audio zone " 162 + mZoneId); 163 } 164 mHandler.sendMessage(mHandler.obtainMessage(MSG_VOLUME_CHANGED)); 165 } 166 requestFocus(int groupId)167 public void requestFocus(int groupId) { 168 // Automatic volume change only works for primary audio zone. 169 if (mZoneId == CarAudioManager.PRIMARY_AUDIO_ZONE) { 170 mHandler.sendMessage(mHandler.obtainMessage(MSG_REQUEST_FOCUS, groupId)); 171 } 172 } 173 174 private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener { 175 private final int mGroupId; AudioFocusListener(int groupId)176 AudioFocusListener(int groupId) { 177 mGroupId = groupId; 178 } 179 @Override onAudioFocusChange(int focusChange)180 public void onAudioFocusChange(int focusChange) { 181 if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { 182 mHandler.sendMessage(mHandler.obtainMessage(MSG_FOCUS_CHANGED, mGroupId, 0)); 183 } else { 184 Log.e(TAG, "Audio focus request failed"); 185 } 186 } 187 } 188 } 189