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