1 /*
2  * Copyright (C) 2021 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.audio;
18 
19 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
20 
21 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_COMPLETED;
22 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_DELAYED;
23 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_PAUSED;
24 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_STARTED;
25 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_STOPPED;
26 import static com.google.android.car.kitchensink.audio.AudioTestFragment.getAudioLogTag;
27 
28 import android.annotation.Nullable;
29 import android.media.AudioAttributes;
30 import android.media.AudioRouting;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.widget.TextView;
37 
38 import com.android.internal.widget.RecyclerView;
39 
40 import com.google.android.car.kitchensink.R;
41 import com.google.android.car.kitchensink.audio.AudioPlayer.AudioPlayerState;
42 
43 import java.util.List;
44 
45 public final class PlayerAdapter extends RecyclerView.Adapter<PlayerAdapter.ViewHolder> {
46     private static final String TAG = getAudioLogTag(PlayerAdapter.class);
47     private static final long STOP_DELAY_TIME_MS = 3_000;
48 
49     private final List<AudioPlayer> mPlayers;
50     private final Handler mHandler;
51 
PlayerAdapter(List<AudioPlayer> players)52     public PlayerAdapter(List<AudioPlayer> players) {
53         mHandler = new Handler(Looper.getMainLooper());
54         mPlayers = players;
55     }
56 
57     public final class ViewHolder extends RecyclerView.ViewHolder {
58         private final TextView mUsageTextView;
59         private final TextView mStatusTextView;
60         private final TextView mDeviceInfoTextView;
61         private AudioPlayer mPlayer;
62 
ViewHolder(View v)63         private ViewHolder(View v) {
64             super(v);
65             mUsageTextView = v.findViewById(R.id.player_usage);
66             mStatusTextView = v.findViewById(R.id.player_status);
67             mDeviceInfoTextView = v.findViewById(R.id.player_device);
68             v.findViewById(R.id.player_start).setOnClickListener(e -> startPlayer());
69             v.findViewById(R.id.player_play_once).setOnClickListener(e -> {
70                 startPlayer();
71                 mHandler.postDelayed(() -> stopPlayer(), STOP_DELAY_TIME_MS);
72             });
73             v.findViewById(R.id.player_stop).setOnClickListener(e -> stopPlayer());
74             setAudioRoutingInfo(null);
75         }
76 
setPlayer(AudioPlayer audioPlayer)77         private void setPlayer(AudioPlayer audioPlayer) {
78             mPlayer = audioPlayer;
79             mPlayer.setAudioRoutingListener(router -> setAudioRoutingInfo(router));
80             mUsageTextView.setText(AudioAttributes.usageToString(
81                     mPlayer.getUsage()).replace("USAGE_", ""));
82         }
83 
setAudioRoutingInfo(@ullable AudioRouting router)84         private void setAudioRoutingInfo(@Nullable AudioRouting router) {
85             String deviceAddress = "None";
86             if (router != null && router.getRoutedDevice() != null) {
87                 deviceAddress = router.getRoutedDevice().getAddress();
88             }
89             mDeviceInfoTextView.setText(mDeviceInfoTextView.getContext()
90                     .getString(R.string.audio_device_selected, deviceAddress));
91         }
92 
startPlayer()93         private void startPlayer() {
94             mPlayer.start(true, true, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, state -> {
95                 handleStateChange(state);
96             });
97         }
98 
handleStateChange(@udioPlayerState int state)99         private void handleStateChange(@AudioPlayerState int state) {
100             int resourceId;
101             switch (state) {
102                 case PLAYER_STATE_STARTED:
103                     resourceId = R.string.player_started;
104                     break;
105                 case PLAYER_STATE_DELAYED:
106                     resourceId = R.string.player_delayed;
107                     break;
108                 case PLAYER_STATE_PAUSED:
109                     resourceId = R.string.player_paused;
110                     break;
111                 case PLAYER_STATE_STOPPED:
112                 case PLAYER_STATE_COMPLETED:
113                 default:
114                     resourceId = R.string.player_stopped;
115             }
116             mStatusTextView.setText(resourceId);
117 
118             // Device info handled by callback, can return from here.
119             if (state == PLAYER_STATE_STARTED) {
120                 return;
121             }
122             setAudioRoutingInfo(null);
123         }
124 
stopPlayer()125         private void stopPlayer() {
126             mPlayer.stop();
127             mStatusTextView.setText(R.string.player_stopped);
128             setAudioRoutingInfo(null);
129         }
130     }
131 
132     @Override
onCreateViewHolder(ViewGroup viewGroup, int viewType)133     public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
134         View playerView = LayoutInflater.from(viewGroup.getContext())
135                 .inflate(R.layout.audio_player_item, viewGroup, false);
136 
137         return new ViewHolder(playerView);
138     }
139 
140     @Override
onBindViewHolder(ViewHolder viewHolder, final int position)141     public void onBindViewHolder(ViewHolder viewHolder, final int position) {
142         viewHolder.setPlayer(mPlayers.get(position));
143     }
144 
145     @Override
getItemCount()146     public int getItemCount() {
147         return mPlayers.size();
148     }
149 }
150