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.car.media.CarAudioManager.CarVolumeCallback; 22 import android.content.Context; 23 import android.media.AudioManager; 24 import android.os.Bundle; 25 import android.util.Log; 26 import android.util.SparseArray; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.SeekBar; 31 32 import androidx.annotation.Nullable; 33 import androidx.fragment.app.Fragment; 34 import androidx.viewpager.widget.ViewPager; 35 36 import com.google.android.car.kitchensink.R; 37 import com.google.android.material.tabs.TabLayout; 38 39 import java.util.List; 40 41 import javax.annotation.concurrent.GuardedBy; 42 43 public final class VolumeTestFragment extends Fragment { 44 private static final String TAG = "CarVolumeTest"; 45 private static final boolean DEBUG = true; 46 47 private AudioManager mAudioManager; 48 private AudioZoneVolumeTabAdapter mAudioZoneAdapter; 49 @GuardedBy("mLock") 50 private final SparseArray<CarAudioZoneVolumeFragment> mZoneVolumeFragments = 51 new SparseArray<>(); 52 53 private CarAudioManager mCarAudioManager; 54 private Car mCar; 55 56 private SeekBar mFader; 57 private SeekBar mBalance; 58 59 private TabLayout mZonesTabLayout; 60 private Object mLock = new Object(); 61 62 public static final class CarAudioZoneVolumeInfo { 63 public int groupId; 64 public String id; 65 public int maxGain; 66 public int minGain; 67 public String currentGain; 68 public boolean hasAudioFocus; 69 public boolean isMuted; 70 } 71 72 private final class CarVolumeChangeListener extends CarVolumeCallback { 73 @Override onGroupVolumeChanged(int zoneId, int groupId, int flags)74 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 75 if (DEBUG) { 76 Log.d(TAG, "onGroupVolumeChanged volume changed for zone " 77 + zoneId); 78 } 79 sendFragmentChangedMessage(zoneId, groupId, flags); 80 } 81 82 @Override onGroupMuteChanged(int zoneId, int groupId, int flags)83 public void onGroupMuteChanged(int zoneId, int groupId, int flags) { 84 if (DEBUG) { 85 Log.d(TAG, "onGroupMuteChanged mute changed for zone " + zoneId); 86 } 87 sendFragmentChangedMessage(zoneId, groupId, flags); 88 } 89 90 @Override onMasterMuteChanged(int zoneId, int flags)91 public void onMasterMuteChanged(int zoneId, int flags) { 92 if (DEBUG) { 93 Log.d(TAG, "onMasterMuteChanged master mute " 94 + mAudioManager.isMasterMute()); 95 } 96 } 97 sendFragmentChangedMessage(int zoneId, int groupId, int flags)98 private void sendFragmentChangedMessage(int zoneId, int groupId, int flags) { 99 CarAudioZoneVolumeFragment fragment; 100 101 synchronized (mLock) { 102 fragment = mZoneVolumeFragments.get(zoneId); 103 } 104 105 if (fragment != null) { 106 fragment.sendVolumeChangedMessage(groupId, flags); 107 } 108 } 109 } 110 111 private final CarVolumeCallback mCarVolumeCallback = new CarVolumeChangeListener(); 112 113 private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { 114 if (!ready) { 115 if (DEBUG) { 116 Log.d(TAG, "Disconnect from Car Service"); 117 } 118 return; 119 } 120 if (DEBUG) { 121 Log.d(TAG, "Connected to Car Service"); 122 } 123 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 124 initVolumeInfo(); 125 mCarAudioManager.registerCarVolumeCallback(mCarVolumeCallback); 126 }; 127 128 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)129 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 130 @Nullable Bundle savedInstanceState) { 131 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 132 133 View v = inflater.inflate(R.layout.volume_test, container, false); 134 135 mZonesTabLayout = v.findViewById(R.id.zones_tab); 136 ViewPager viewPager = (ViewPager) v.findViewById(R.id.zone_view_pager); 137 138 mAudioZoneAdapter = new AudioZoneVolumeTabAdapter(getChildFragmentManager()); 139 viewPager.setAdapter(mAudioZoneAdapter); 140 mZonesTabLayout.setupWithViewPager(viewPager); 141 142 SeekBar.OnSeekBarChangeListener seekListener = 143 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(), /* handler= */ null, 165 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener); 166 return v; 167 } 168 169 @Override onDestroyView()170 public void onDestroyView() { 171 if (mCar != null && mCar.isConnected()) { 172 mCar.disconnect(); 173 mCar = null; 174 } 175 super.onDestroyView(); 176 } 177 initVolumeInfo()178 private void initVolumeInfo() { 179 synchronized (mLock) { 180 List<Integer> audioZoneIds = mCarAudioManager.getAudioZoneIds(); 181 for (int index = 0; index < audioZoneIds.size(); index++) { 182 int zoneId = audioZoneIds.get(index); 183 CarAudioZoneVolumeFragment fragment = 184 new CarAudioZoneVolumeFragment(zoneId, mCarAudioManager, mAudioManager); 185 mZonesTabLayout.addTab(mZonesTabLayout.newTab().setText("Audio Zone " + zoneId)); 186 mAudioZoneAdapter.addFragment(fragment, "Audio Zone " + zoneId); 187 if (DEBUG) { 188 Log.d(TAG, "Adding audio volume for zone " + zoneId); 189 } 190 mZoneVolumeFragments.put(zoneId, fragment); 191 } 192 } 193 } 194 } 195