package com.gaiagps.iburn;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.widget.Toast;
import org.prx.playerhater.PlayerHater;
import org.prx.playerhater.PlayerHaterListener;
import org.prx.playerhater.Song;
import org.prx.playerhater.plugins.PlayerHaterListenerPlugin;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import rx.Observable;
import rx.schedulers.Schedulers;
import timber.log.Timber;
/**
* Audio tour playback manager
* Created by dbro on 8/5/16.
*/
public class AudioTourManager {
private final OkHttpClient http = new OkHttpClient();
private Context context;
private PlayerHater player;
private Uri defaultAlbumArtUri;
private File mediaArtDir;
private File mediaDir;
public static File getAudioTourDirectory(@NonNull Context context) {
File audioTourDir = new File(context.getFilesDir(), "audio_tour");
audioTourDir.mkdirs();
return audioTourDir;
}
public static File getAudioTourArtDirectory(@NonNull Context context) {
File audioTourArtDir = new File(context.getFilesDir(), "audio_tour_art");
audioTourArtDir.mkdirs();
return audioTourArtDir;
}
/**
* Create a new AudioTourManager bound to the given Context.
* Generally call this on {@link Activity#onResume()}
*
* @param context The Context to bind the player service to
*/
public AudioTourManager(@NonNull Context context, @Nullable PlayerHaterListener listener) {
this.context = context;
player = PlayerHater.bind(context);
player.setLocalPlugin(new PlayerHaterListenerPlugin(listener));
defaultAlbumArtUri = getResourceUri(context, R.drawable.iburn_logo);
mediaArtDir = getAudioTourArtDirectory(context);
mediaDir = getAudioTourDirectory(context);
if (!mediaDir.exists() && !mediaArtDir.exists()) {
Timber.e("Failed to create audio tour directories!");
}
}
/**
* Unbind the player
*/
public void release() {
player.release();
}
public void playAudioTourUrl(@NonNull String audioTourUrl, @Nullable String title) {
File cachedFile = getCachedFileForRemoteMediaPath(audioTourUrl);
if (!cachedFile.exists()) {
Toast.makeText(context, "Downloading '" + title + "' Tour. Playback will start when ready", Toast.LENGTH_LONG).show();
cacheAndPlayRemoteMediaPath(audioTourUrl, title);
} else {
playLocalMediaUrl(Uri.fromFile(cachedFile), title);
}
}
public Uri getCurrentAudioTourUrl() {
return player.nowPlaying() != null ? player.nowPlaying().getUri() : null;
}
public void pause() {
player.pause();
}
public Song songFromAudioTourUrl(@NonNull Uri audioTourUri,
@Nullable String title) {
return new Song() {
@Override
public String getTitle() {
return title;
}
@Override
public String getArtist() {
return "Jim Tierney";
}
@Override
public String getAlbumTitle() {
return "Art Discovery Audio Guide";
}
@Override
public Uri getAlbumArt() {
Uri artUri = getFileImage(audioTourUri);
context.grantUriPermission("com.android.systemui", artUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Timber.d("Returning album art uri %s", artUri);
return artUri;
}
@Override
public Uri getUri() {
return audioTourUri;
}
@Override
public Bundle getExtra() {
return null;
}
};
}
private void playLocalMediaUrl(@NonNull Uri localMediaUri, @Nullable String title) {
Uri curTourUrl = getCurrentAudioTourUrl();
if (curTourUrl != null && curTourUrl.equals(localMediaUri)) {
player.play();
} else {
player.play(songFromAudioTourUrl(localMediaUri, title));
}
}
private static Uri getResourceUri(@NonNull Context context, int resID) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
context.getResources().getResourcePackageName(resID) + '/' +
context.getResources().getResourceTypeName(resID) + '/' +
resID);
}
private Uri getFileImage(@NonNull Uri remoteMediaPath) {
File artFile = getMediaArtFileForMediaPath(remoteMediaPath);
if (artFile.exists()) {
return getContentUriForFile(artFile);
} else {
Timber.d("Parsing image for audio %s into %s", remoteMediaPath, artFile.getAbsolutePath());
MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
metadataRetriever.setDataSource(context, remoteMediaPath);
byte[] data = metadataRetriever.getEmbeddedPicture();
if (data != null) {
Timber.d("Got %d byte embedded picture for %s", data.length, remoteMediaPath);
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
try {
FileOutputStream fos = new FileOutputStream(artFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos);
bitmap.recycle();
return getContentUriForFile(artFile);
} catch (FileNotFoundException e) {
Timber.e(e, "Failed to save album art");
}
}
return defaultAlbumArtUri;
}
}
private Uri getContentUriForFile(@NonNull File file) {
return FileProvider.getUriForFile(
context,
"com.gaiagps.iburn.fileprovider",
file);
}
private File getMediaArtFileForMediaPath(@NonNull Uri mediaPath) {
String strPath = mediaPath.toString();
return new File(mediaArtDir, strPath.substring(strPath.lastIndexOf("/"), strPath.lastIndexOf(".")) + ".jpg");
}
private File getCachedFileForRemoteMediaPath(@NonNull String mediaPath) {
return new File(mediaDir, mediaPath.substring(mediaPath.lastIndexOf("/"), mediaPath.length()));
}
static File getCachedFileForRemoteMediaPath(@NonNull Context context, @NonNull String mediaPath) {
return new File(getAudioTourDirectory(context), mediaPath.substring(mediaPath.lastIndexOf("/"), mediaPath.length()));
}
private void cacheAndPlayRemoteMediaPath(@NonNull String remoteMediaPath, @Nullable String title) {
Observable.just(remoteMediaPath)
.observeOn(Schedulers.io())
.subscribe(path -> {
boolean didCache = cacheRemoteMediaPath(context, http, remoteMediaPath);
if (didCache) {
File cachedFile = getCachedFileForRemoteMediaPath(remoteMediaPath);
playLocalMediaUrl(Uri.fromFile(cachedFile), title);
}
});
}
static boolean cacheRemoteMediaPath(@NonNull Context context, @NonNull OkHttpClient http, @NonNull String remoteMediaPath) {
try {
Request request = new Request.Builder()
.url(remoteMediaPath)
.build();
long startTime = System.currentTimeMillis();
Response response = http.newCall(request).execute();
if (!response.isSuccessful()) {
Timber.e("Media download of %s reported unexpected code %s", remoteMediaPath, response);
return false;
} else {
Timber.d("Downloaded %s in %s ms", remoteMediaPath, System.currentTimeMillis() - startTime);
}
File destFile = getCachedFileForRemoteMediaPath(context, remoteMediaPath);
FileOutputStream os = new FileOutputStream(destFile);
InputStream is = response.body().byteStream();
byte[] buff = new byte[1024];
int bytesRead = 0;
while ((bytesRead = is.read(buff)) > 0) {
os.write(buff, 0, bytesRead);
}
is.close();
os.close();
return true;
} catch (IOException e) {
Timber.e(e, "Failed to fetch %s", remoteMediaPath);
}
return false;
}
}