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