package com.codegy.aerlink.services.media;
import android.app.Notification;
import android.app.PendingIntent;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.VolumeProvider;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log;
import com.codegy.aerlink.Constants;
import com.codegy.aerlink.R;
import com.codegy.aerlink.connection.command.Command;
import com.codegy.aerlink.utils.ServiceHandler;
import com.codegy.aerlink.utils.ServiceUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Created by Guiye on 19/5/15.
*/
public class MediaServiceHandler extends ServiceHandler {
private static final String LOG_TAG = MediaServiceHandler.class.getSimpleName();
public static final int NOTIFICATION_MEDIA = 1001;
private Context mContext;
private ServiceUtils mServiceUtils;
private MediaSession mSession;
private boolean mediaPlaying;
private boolean mediaHidden = true;
private String mediaTitle;
private String mediaArtist;
public MediaServiceHandler(Context context, ServiceUtils serviceUtils) {
this.mContext = context;
this.mServiceUtils = serviceUtils;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Constants.IA_HIDE_MEDIA);
context.registerReceiver(mBroadcastReceiver, intentFilter);
// Run on main thread
final Handler handler = new Handler(mContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
prepareMediaSession();
handler.removeCallbacksAndMessages(null);
}
});
}
@Override
public void close() {
mediaTitle = null;
mediaArtist = null;
if (mSession != null) {
mSession.release();
mSession = null;
}
mContext.unregisterReceiver(mBroadcastReceiver);
}
@Override
public UUID getServiceUUID() {
return AMSConstants.SERVICE_UUID;
}
@Override
public List<String> getCharacteristicsToSubscribe() {
List<String> characteristics = new ArrayList<>();
characteristics.add(AMSConstants.CHARACTERISTIC_ENTITY_UPDATE);
// characteristics.add(AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE);
return characteristics;
}
@Override
public boolean canHandleCharacteristic(BluetoothGattCharacteristic characteristic) {
String characteristicUUID = characteristic.getUuid().toString().toLowerCase();
return characteristicUUID.equals(AMSConstants.CHARACTERISTIC_ENTITY_UPDATE) || characteristicUUID.equals(AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE);
}
@Override
public void handleCharacteristic(BluetoothGattCharacteristic characteristic) {
try {
byte[] packet = characteristic.getValue();
if (packet != null) {
String attribute = characteristic.getStringValue(3);
Log.d(LOG_TAG, "AMS ATTRIBUTE: " + attribute);
switch (packet[0]) {
case 0:
switch (packet[1]) {
case 1:
if (attribute.length() > 0) {
mediaPlaying = !attribute.substring(0, 1).equals("0");
if (attribute.equals("0,,")) {
mediaHidden = true;
mServiceUtils.cancelNotification(null, NOTIFICATION_MEDIA);
}
}
break;
}
updatePlaybackState();
break;
case 2:
boolean truncated = (packet[2] & 1) != 0;
switch (packet[1]) {
case 0:
mediaArtist = attribute;
if (truncated) {
mediaArtist += "...";
Command attributeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE, new byte[] {
AMSConstants.EntityIDTrack,
AMSConstants.TrackAttributeIDArtist
});
attributeCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(attributeCommand);
Command readAttributeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE);
readAttributeCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(readAttributeCommand);
}
else if (attribute.length() == 0) {
mediaArtist = null;
}
break;
case 2:
mediaTitle = attribute;
if (truncated) {
mediaTitle += "...";
Command attributeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE, new byte[] {
AMSConstants.EntityIDTrack,
AMSConstants.TrackAttributeIDTitle
});
attributeCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(attributeCommand);
Command readAttributeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_ENTITY_ATTRIBUTE);
readAttributeCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(readAttributeCommand);
}
else if (attribute.length() == 0) {
mediaTitle = null;
}
break;
}
updateMetadata();
break;
}
if (mediaPlaying || !mediaHidden) {
buildMediaNotification();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public void sendPlay() {
Command remoteCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDTogglePlayPause
});
remoteCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(remoteCommand);
}
private void buildMediaNotification() {
mediaHidden = false;
Bitmap background = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg_media);
// Build pending intent for when the user swipes the card away
Intent deleteIntent = new Intent(Constants.IA_HIDE_MEDIA);
PendingIntent deleteAction = PendingIntent.getBroadcast(mContext, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.MediaStyle style = new Notification.MediaStyle()
.setMediaSession(mSession.getSessionToken());
Notification.WearableExtender wearableExtender = new Notification.WearableExtender()
.setBackground(background)
.setHintHideIcon(true);
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.nic_music)
.setDeleteIntent(deleteAction)
.setStyle(style)
.extend(wearableExtender)
.setPriority(Notification.PRIORITY_LOW);
//notificationManager.cancel(NOTIFICATION_MEDIA);
mServiceUtils.notify(null, NOTIFICATION_MEDIA, builder.build());
}
private void prepareMediaSession() {
mSession = new MediaSession(mContext, "Aerlink_session");
mSession.setActive(true);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
mSession.setPlaybackToRemote(new VolumeProvider(VolumeProvider.VOLUME_CONTROL_RELATIVE, 50, 100) {
@Override
public void onAdjustVolume(int direction) {
super.onAdjustVolume(direction);
try {
Command volumeCommand;
if (direction == 1) {
volumeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDVolumeUp
});
} else {
volumeCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDVolumeDown
});
}
mServiceUtils.addCommandToQueue(volumeCommand);
} catch (Exception e) {
e.printStackTrace();
}
}
});
mSession.setCallback(new MediaSession.Callback() {
@Override
public void onPlay() {
super.onPlay();
Log.e(LOG_TAG, "onPlay");
Command remoteCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDTogglePlayPause
});
remoteCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(remoteCommand);
}
@Override
public void onPause() {
super.onPause();
Log.e(LOG_TAG, "onPause");
Command remoteCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDTogglePlayPause
});
remoteCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(remoteCommand);
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
Log.e(LOG_TAG, "onSkipToNext");
Command remoteCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDNextTrack
});
remoteCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(remoteCommand);
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
Log.e(LOG_TAG, "onSkipToPrevious");
Command remoteCommand = new Command(AMSConstants.SERVICE_UUID, AMSConstants.CHARACTERISTIC_REMOTE_COMMAND, new byte[]{
AMSConstants.RemoteCommandIDPreviousTrack
});
remoteCommand.setImportance(Command.IMPORTANCE_MIN);
mServiceUtils.addCommandToQueue(remoteCommand);
}
@Override
public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
return super.onMediaButtonEvent(mediaButtonIntent);
}
});
updatePlaybackState();
updateMetadata();
}
private void updatePlaybackState() {
long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
.setActions(PlaybackState.ACTION_PLAY_PAUSE
| PlaybackState.ACTION_SKIP_TO_PREVIOUS
| PlaybackState.ACTION_SKIP_TO_NEXT);
stateBuilder.setState(mediaPlaying ? PlaybackState.STATE_PLAYING : PlaybackState.STATE_PAUSED, position, 1.0f);
mSession.setPlaybackState(stateBuilder.build());
}
private void updateMetadata() {
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
if (mediaTitle == null && mediaArtist == null) {
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, "No info");
}
else {
// And at minimum the title and artist for legacy support
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, mediaTitle);
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, mediaArtist);
}
// Add any other fields you have for your data as well
mSession.setMetadata(metadataBuilder.build());
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mediaHidden = true;
if (mediaPlaying) {
buildMediaNotification();
}
}
};
}