VolumeTestFragment.java revision 96def40502ff34f01d11eeae49e63582ec560dda
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 */
16package com.google.android.car.kitchensink.volume;
17
18import android.car.Car;
19import android.car.CarNotConnectedException;
20import android.car.media.CarAudioManager;
21import android.content.Context;
22import android.media.AudioManager;
23import android.media.IVolumeController;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Message;
27import android.os.RemoteException;
28import android.support.annotation.Nullable;
29import android.support.v4.app.Fragment;
30import android.util.Log;
31import android.util.SparseIntArray;
32import android.view.KeyEvent;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.view.ViewGroup;
36import android.widget.Button;
37import android.widget.ListView;
38
39import com.google.android.car.kitchensink.CarEmulator;
40import com.google.android.car.kitchensink.R;
41
42public class VolumeTestFragment extends Fragment{
43    private static final String TAG = "CarVolumeTest";
44    private static final int MSG_VOLUME_CHANGED = 0;
45    private static final int MSG_REQUEST_FOCUS = 1;
46    private static final int MSG_FOCUS_CHANGED= 2;
47
48    private ListView mVolumeList;
49    private Button mRefreshButton;
50    private AudioManager mAudioManager;
51    private VolumeAdapter mAdapter;
52
53    private CarAudioManager mCarAudioManager;
54    private Car mCar;
55    private CarEmulator mCarEmulator;
56
57    private Button mVolumeUp;
58    private Button mVolumeDown;
59
60    private final VolumeController mVolumeController = new VolumeController();
61    private final Handler mHandler = new VolumeHandler();
62
63    private class VolumeHandler extends Handler {
64        private AudioFocusListener mFocusListener;
65
66        @Override
67        public void handleMessage(Message msg) {
68            switch (msg.what) {
69                case MSG_VOLUME_CHANGED:
70                    initVolumeInfo();
71                    break;
72                case MSG_REQUEST_FOCUS:
73                    int stream = msg.arg1;
74                    if (mFocusListener != null) {
75                        mAudioManager.abandonAudioFocus(mFocusListener);
76                        mVolumeInfos[mStreamIndexMap.get(stream)].mHasFocus = false;
77                        mAdapter.notifyDataSetChanged();
78                    }
79
80                    mFocusListener = new AudioFocusListener(stream);
81                    mAudioManager.requestAudioFocus(mFocusListener, stream,
82                            AudioManager.AUDIOFOCUS_GAIN);
83                    break;
84                case MSG_FOCUS_CHANGED:
85                    int focusStream = msg.arg1;
86                    mVolumeInfos[mStreamIndexMap.get(focusStream)].mHasFocus = true;
87                    mAdapter.refreshVolumes(mVolumeInfos);
88                    break;
89
90            }
91        }
92    }
93
94    private VolumeInfo[] mVolumeInfos = new VolumeInfo[LOGICAL_STREAMS.length + 1];
95    private SparseIntArray mStreamIndexMap = new SparseIntArray(LOGICAL_STREAMS.length);
96
97    private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
98        private final int mStream;
99        public AudioFocusListener(int stream) {
100            mStream = stream;
101        }
102        @Override
103        public void onAudioFocusChange(int focusChange) {
104            if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
105                mHandler.sendMessage(mHandler.obtainMessage(MSG_FOCUS_CHANGED, mStream, 0));
106            } else {
107                Log.e(TAG, "Audio focus request failed");
108            }
109        }
110    }
111
112    public static class VolumeInfo {
113        public int logicalStream;
114        public String mId;
115        public String mMax;
116        public String mCurrent;
117        public String mLogicalMax;
118        public String mLogicalCurrent;
119        public boolean mHasFocus;
120    }
121
122    private static final int LOGICAL_STREAMS[] = {
123            AudioManager.STREAM_MUSIC,
124            AudioManager.STREAM_ALARM,
125            AudioManager.STREAM_NOTIFICATION,
126            AudioManager.STREAM_RING,
127            AudioManager.STREAM_VOICE_CALL,
128            AudioManager.STREAM_SYSTEM
129            //            AudioManager.STREAM_DTMF,
130    };
131
132    private static String streamToName (int stream) {
133        switch (stream) {
134            case AudioManager.STREAM_ALARM: return "Alarm";
135            case AudioManager.STREAM_MUSIC: return "Music";
136            case AudioManager.STREAM_NOTIFICATION: return "Notification";
137            case AudioManager.STREAM_RING: return "Ring";
138            case AudioManager.STREAM_VOICE_CALL: return "Call";
139            case AudioManager.STREAM_SYSTEM: return "System";
140            default: return "Unknown";
141        }
142    }
143
144    @Override
145    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
146                             @Nullable Bundle savedInstanceState) {
147        View v = inflater.inflate(R.layout.volume_test, container, false);
148
149        mVolumeList = (ListView) v.findViewById(R.id.volume_list);
150        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
151
152        mRefreshButton = (Button) v.findViewById(R.id.refresh);
153        mAdapter = new VolumeAdapter(getContext(), R.layout.volume_item, mVolumeInfos, this);
154        mVolumeList.setAdapter(mAdapter);
155
156        mRefreshButton.setOnClickListener(new View.OnClickListener() {
157            @Override
158            public void onClick(View view) {
159                initVolumeInfo();
160            }
161        });
162
163        mCarEmulator = CarEmulator.create(getContext());
164        mCar = mCarEmulator.getCar();
165        try {
166            mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
167            initVolumeInfo();
168            mCarAudioManager.setVolumeController(mVolumeController);
169        } catch (CarNotConnectedException e) {
170            throw new RuntimeException(e); // Should never occur in car emulator.
171        }
172
173        mVolumeUp = (Button) v.findViewById(R.id.volume_up);
174        mVolumeDown = (Button) v.findViewById(R.id.volume_down);
175
176        mVolumeUp.setOnClickListener(new View.OnClickListener() {
177            @Override
178            public void onClick(View view) {
179                mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_UP);
180            }
181        });
182
183        mVolumeDown.setOnClickListener(new View.OnClickListener() {
184            @Override
185            public void onClick(View view) {
186                mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_DOWN);
187            }
188        });
189
190        return v;
191    }
192
193    public void adjustVolumeByOne(int logicalStream, boolean up) {
194        if (mCarAudioManager == null) {
195            Log.e(TAG, "CarAudioManager is null");
196            return;
197        }
198        int current = 0;
199        try {
200            current = mCarAudioManager.getStreamVolume(logicalStream);
201        } catch (CarNotConnectedException e) {
202            Log.e(TAG, "car not connected", e);
203        }
204        setStreamVolume(logicalStream, current + (up ? 1 : -1));
205    }
206
207    public void requestFocus(int logicalStream) {
208        mHandler.sendMessage(mHandler.obtainMessage(MSG_REQUEST_FOCUS, logicalStream));
209    }
210
211    public void setStreamVolume(int logicalStream, int volume) {
212        if (mCarAudioManager == null) {
213            Log.e(TAG, "CarAudioManager is null");
214            return;
215        }
216        try {
217            mCarAudioManager.setStreamVolume(logicalStream, volume, 0);
218        } catch (CarNotConnectedException e) {
219            Log.e(TAG, "car not connected", e);
220        }
221
222        Log.d(TAG, "Set stream " + logicalStream + " volume " + volume);
223    }
224
225
226    private void initVolumeInfo() {
227        if (mVolumeInfos[0] == null) {
228            mVolumeInfos[0] = new VolumeInfo();
229            mVolumeInfos[0].mId = "Stream";
230            mVolumeInfos[0].mCurrent = "Current";
231            mVolumeInfos[0].mMax = "Max";
232            mVolumeInfos[0].mLogicalMax = "Android_Max";
233            mVolumeInfos[0].mLogicalCurrent = "Android_Current";
234
235        }
236        int i = 1;
237        for (int stream : LOGICAL_STREAMS) {
238            if (mVolumeInfos[i] == null) {
239                mVolumeInfos[i] = new VolumeInfo();
240            }
241            mVolumeInfos[i].logicalStream = stream;
242            mStreamIndexMap.put(stream, i);
243            mVolumeInfos[i].mId = streamToName(stream);
244
245            int current = 0;
246            int max = 0;
247            try {
248                current = mCarAudioManager.getStreamVolume(stream);
249                max = mCarAudioManager.getStreamMaxVolume(stream);
250            } catch (CarNotConnectedException e) {
251                Log.e(TAG, "car not connected", e);
252            }
253
254            mVolumeInfos[i].mCurrent = String.valueOf(current);
255            mVolumeInfos[i].mMax = String.valueOf(max);
256
257            mVolumeInfos[i].mLogicalMax = String.valueOf(mAudioManager.getStreamMaxVolume(stream));
258            mVolumeInfos[i].mLogicalCurrent = String.valueOf(mAudioManager.getStreamVolume(stream));
259
260            Log.d(TAG, stream + " max: " + mVolumeInfos[i].mMax + " current: "
261                    + mVolumeInfos[i].mCurrent);
262            i++;
263        }
264        mAdapter.refreshVolumes(mVolumeInfos);
265    }
266
267    @Override
268    public void onDestroy() {
269        if (mCar != null) {
270            mCar.disconnect();
271        }
272        super.onDestroy();
273    }
274
275    private class VolumeController extends IVolumeController.Stub {
276
277        @Override
278        public void displaySafeVolumeWarning(int flags) throws RemoteException {}
279
280        @Override
281        public void volumeChanged(int streamType, int flags) throws RemoteException {
282            mHandler.sendMessage(mHandler.obtainMessage(MSG_VOLUME_CHANGED, streamType));
283        }
284
285        @Override
286        public void masterMuteChanged(int flags) throws RemoteException {}
287
288        @Override
289        public void setLayoutDirection(int layoutDirection) throws RemoteException {
290        }
291
292        @Override
293        public void dismiss() throws RemoteException {
294        }
295
296        @Override
297        public void setA11yMode(int mode) throws RemoteException {
298        }
299    }
300}
301