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 class CarAudioZoneVolumeInfo { 63 public int mGroupId; 64 public String mId; 65 public String mMax; 66 public String mCurrent; 67 public boolean mHasFocus; 68 } 69 70 private final class CarVolumeChangeListener extends CarVolumeCallback { 71 @Override onGroupVolumeChanged(int zoneId, int groupId, int flags)72 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 73 if (DEBUG) { 74 Log.d(TAG, "onGroupVolumeChanged volume changed for zone " 75 + zoneId); 76 } 77 synchronized (mLock) { 78 CarAudioZoneVolumeFragment fragment = mZoneVolumeFragments.get(zoneId); 79 if (fragment != null) { 80 fragment.sendChangeMessage(); 81 } 82 } 83 } 84 85 @Override onMasterMuteChanged(int zoneId, int flags)86 public void onMasterMuteChanged(int zoneId, int flags) { 87 if (DEBUG) { 88 Log.d(TAG, "onMasterMuteChanged master mute " 89 + mAudioManager.isMasterMute()); 90 } 91 } 92 } 93 94 private final CarVolumeCallback mCarVolumeCallback = new CarVolumeChangeListener(); 95 96 private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { 97 if (!ready) { 98 if (DEBUG) { 99 Log.d(TAG, "Disconnect from Car Service"); 100 } 101 return; 102 } 103 if (DEBUG) { 104 Log.d(TAG, "Connected to Car Service"); 105 } 106 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 107 initVolumeInfo(); 108 mCarAudioManager.registerCarVolumeCallback(mCarVolumeCallback); 109 }; 110 111 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)112 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 113 @Nullable Bundle savedInstanceState) { 114 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 115 116 View v = inflater.inflate(R.layout.volume_test, container, false); 117 118 mZonesTabLayout = v.findViewById(R.id.zones_tab); 119 ViewPager viewPager = (ViewPager) v.findViewById(R.id.zone_view_pager); 120 121 mAudioZoneAdapter = new AudioZoneVolumeTabAdapter(getChildFragmentManager()); 122 viewPager.setAdapter(mAudioZoneAdapter); 123 mZonesTabLayout.setupWithViewPager(viewPager); 124 125 SeekBar.OnSeekBarChangeListener seekListener = 126 new SeekBar.OnSeekBarChangeListener() { 127 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 128 final float percent = (progress - 100) / 100.0f; 129 if (seekBar.getId() == R.id.fade_bar) { 130 mCarAudioManager.setFadeTowardFront(percent); 131 } else { 132 mCarAudioManager.setBalanceTowardRight(percent); 133 } 134 } 135 136 public void onStartTrackingTouch(SeekBar seekBar) {} 137 138 public void onStopTrackingTouch(SeekBar seekBar) {} 139 }; 140 141 mFader = v.findViewById(R.id.fade_bar); 142 mFader.setOnSeekBarChangeListener(seekListener); 143 144 mBalance = v.findViewById(R.id.balance_bar); 145 mBalance.setOnSeekBarChangeListener(seekListener); 146 147 mCar = Car.createCar(getActivity(), /* handler= */ null, 148 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener); 149 return v; 150 } 151 152 @Override onDestroyView()153 public void onDestroyView() { 154 if (mCar != null && mCar.isConnected()) { 155 mCar.disconnect(); 156 mCar = null; 157 } 158 super.onDestroyView(); 159 } 160 initVolumeInfo()161 private void initVolumeInfo() { 162 synchronized (mLock) { 163 List<Integer> audioZoneIds = mCarAudioManager.getAudioZoneIds(); 164 for (int index = 0; index < audioZoneIds.size(); index++) { 165 int zoneId = audioZoneIds.get(index); 166 CarAudioZoneVolumeFragment fragment = 167 new CarAudioZoneVolumeFragment(zoneId, mCarAudioManager, mAudioManager); 168 mZonesTabLayout.addTab(mZonesTabLayout.newTab().setText("Audio Zone " + zoneId)); 169 mAudioZoneAdapter.addFragment(fragment, "Audio Zone " + zoneId); 170 if (DEBUG) { 171 Log.d(TAG, "Adding audio volume for zone " + zoneId); 172 } 173 mZoneVolumeFragments.put(zoneId, fragment); 174 } 175 } 176 } 177 } 178