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