1 /*
2  * Copyright (C) 2016 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.android.bluetooth.avrcpcontroller;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.net.Uri;
21 import android.os.SystemClock;
22 import android.support.v4.media.session.MediaSessionCompat;
23 import android.support.v4.media.session.PlaybackStateCompat;
24 import android.util.Log;
25 
26 import java.util.Arrays;
27 
28 /*
29  * Contains information about remote player
30  */
31 class AvrcpPlayer {
32     private static final String TAG = "AvrcpPlayer";
33     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
34 
35     public static final int INVALID_ID = -1;
36 
37     public static final int FEATURE_PLAY = 40;
38     public static final int FEATURE_STOP = 41;
39     public static final int FEATURE_PAUSE = 42;
40     public static final int FEATURE_REWIND = 44;
41     public static final int FEATURE_FAST_FORWARD = 45;
42     public static final int FEATURE_FORWARD = 47;
43     public static final int FEATURE_PREVIOUS = 48;
44     public static final int FEATURE_BROWSING = 59;
45     public static final int FEATURE_NOW_PLAYING = 65;
46 
47     private BluetoothDevice mDevice;
48     private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
49     private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
50     private long mPlayTimeUpdate = 0;
51     private float mPlaySpeed = 1;
52     private int mId;
53     private String mName = "";
54     private int mPlayerType;
55     private byte[] mPlayerFeatures = new byte[16];
56     private long mAvailableActions = PlaybackStateCompat.ACTION_PREPARE;
57     private AvrcpItem mCurrentTrack;
58     private PlaybackStateCompat mPlaybackStateCompat;
59     private PlayerApplicationSettings mSupportedPlayerApplicationSettings =
60             new PlayerApplicationSettings();
61     private PlayerApplicationSettings mCurrentPlayerApplicationSettings;
62 
AvrcpPlayer()63     AvrcpPlayer() {
64         mDevice = null;
65         mId = INVALID_ID;
66         //Set Default Actions in case Player data isn't available.
67         mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY
68                 | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
69                 | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
70                 | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PREPARE;
71         PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
72                 .setActions(mAvailableActions);
73         mPlaybackStateCompat = playbackStateBuilder.build();
74     }
75 
AvrcpPlayer(BluetoothDevice device, int id, String name, byte[] playerFeatures, int playStatus, int playerType)76     AvrcpPlayer(BluetoothDevice device, int id, String name, byte[] playerFeatures, int playStatus,
77             int playerType) {
78         mDevice = device;
79         mId = id;
80         mName = name;
81         mPlayStatus = playStatus;
82         mPlayerType = playerType;
83         mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
84         PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
85                 .setActions(mAvailableActions);
86         mPlaybackStateCompat = playbackStateBuilder.build();
87         updateAvailableActions();
88     }
89 
getDevice()90     public BluetoothDevice getDevice() {
91         return mDevice;
92     }
93 
getId()94     public int getId() {
95         return mId;
96     }
97 
getName()98     public String getName() {
99         return mName;
100     }
101 
setPlayTime(int playTime)102     public void setPlayTime(int playTime) {
103         mPlayTime = playTime;
104         mPlayTimeUpdate = SystemClock.elapsedRealtime();
105         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
106                 mPlayStatus, mPlayTime,
107                 mPlaySpeed).build();
108     }
109 
getPlayTime()110     public long getPlayTime() {
111         return mPlayTime;
112     }
113 
setPlayStatus(int playStatus)114     public void setPlayStatus(int playStatus) {
115         mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
116                 - mPlaybackStateCompat.getLastPositionUpdateTime());
117         mPlayStatus = playStatus;
118         switch (mPlayStatus) {
119             case PlaybackStateCompat.STATE_STOPPED:
120                 mPlaySpeed = 0;
121                 break;
122             case PlaybackStateCompat.STATE_PLAYING:
123                 mPlaySpeed = 1;
124                 break;
125             case PlaybackStateCompat.STATE_PAUSED:
126                 mPlaySpeed = 0;
127                 break;
128             case PlaybackStateCompat.STATE_FAST_FORWARDING:
129                 mPlaySpeed = 3;
130                 break;
131             case PlaybackStateCompat.STATE_REWINDING:
132                 mPlaySpeed = -3;
133                 break;
134         }
135 
136         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
137                 mPlayStatus, mPlayTime,
138                 mPlaySpeed).build();
139     }
140 
setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)141     public void setSupportedPlayerApplicationSettings(
142             PlayerApplicationSettings playerApplicationSettings) {
143         mSupportedPlayerApplicationSettings = playerApplicationSettings;
144         updateAvailableActions();
145     }
146 
setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)147     public void setCurrentPlayerApplicationSettings(
148             PlayerApplicationSettings playerApplicationSettings) {
149         Log.d(TAG, "Settings changed");
150         mCurrentPlayerApplicationSettings = playerApplicationSettings;
151         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
152         session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting(
153                 PlayerApplicationSettings.REPEAT_STATUS));
154         session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting(
155                 PlayerApplicationSettings.SHUFFLE_STATUS));
156     }
157 
getPlayStatus()158     public int getPlayStatus() {
159         return mPlayStatus;
160     }
161 
supportsFeature(int featureId)162     public boolean supportsFeature(int featureId) {
163         int byteNumber = featureId / 8;
164         byte bitMask = (byte) (1 << (featureId % 8));
165         return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
166     }
167 
supportsSetting(int settingType, int settingValue)168     public boolean supportsSetting(int settingType, int settingValue) {
169         return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue);
170     }
171 
getPlaybackState()172     public PlaybackStateCompat getPlaybackState() {
173         if (DBG) {
174             Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
175         }
176         return mPlaybackStateCompat;
177     }
178 
updateCurrentTrack(AvrcpItem update)179     public synchronized void updateCurrentTrack(AvrcpItem update) {
180         if (update != null) {
181             long trackNumber = update.getTrackNumber();
182             mPlaybackStateCompat = new PlaybackStateCompat.Builder(
183                     mPlaybackStateCompat).setActiveQueueItemId(
184                     trackNumber - 1).build();
185         }
186         mCurrentTrack = update;
187     }
188 
notifyImageDownload(String uuid, Uri imageUri)189     public synchronized boolean notifyImageDownload(String uuid, Uri imageUri) {
190         if (DBG) Log.d(TAG, "Got an image download -- uuid=" + uuid + ", uri=" + imageUri);
191         if (uuid == null || imageUri == null || mCurrentTrack == null) return false;
192         if (uuid.equals(mCurrentTrack.getCoverArtUuid())) {
193             mCurrentTrack.setCoverArtLocation(imageUri);
194             if (DBG) Log.d(TAG, "Image UUID '" + uuid + "' was added to current track.");
195             return true;
196         }
197         return false;
198     }
199 
getCurrentTrack()200     public synchronized AvrcpItem getCurrentTrack() {
201         return mCurrentTrack;
202     }
203 
updateAvailableActions()204     private void updateAvailableActions() {
205         if (supportsFeature(FEATURE_PLAY)) {
206             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
207         }
208         if (supportsFeature(FEATURE_STOP)) {
209             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP;
210         }
211         if (supportsFeature(FEATURE_PAUSE)) {
212             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE;
213         }
214         if (supportsFeature(FEATURE_REWIND)) {
215             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND;
216         }
217         if (supportsFeature(FEATURE_FAST_FORWARD)) {
218             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD;
219         }
220         if (supportsFeature(FEATURE_FORWARD)) {
221             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
222         }
223         if (supportsFeature(FEATURE_PREVIOUS)) {
224             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
225         }
226         if (mSupportedPlayerApplicationSettings.supportsSetting(
227                 PlayerApplicationSettings.REPEAT_STATUS)) {
228             mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
229         }
230         if (mSupportedPlayerApplicationSettings.supportsSetting(
231                 PlayerApplicationSettings.SHUFFLE_STATUS)) {
232             mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
233         }
234         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat)
235                 .setActions(mAvailableActions).build();
236 
237         if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
238     }
239 }
240