package tv.emby.embyatv.integration;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import mediabrowser.apiinteraction.Response;
import mediabrowser.model.dto.BaseItemDto;
import mediabrowser.model.entities.LocationType;
import mediabrowser.model.entities.SortOrder;
import mediabrowser.model.querying.ItemFields;
import mediabrowser.model.querying.ItemFilter;
import mediabrowser.model.querying.ItemSortBy;
import mediabrowser.model.querying.ItemsResult;
import mediabrowser.model.querying.NextUpQuery;
import mediabrowser.model.querying.SimilarItemsQuery;
import tv.emby.embyatv.R;
import tv.emby.embyatv.TvApp;
import tv.emby.embyatv.browsing.MainActivity;
import tv.emby.embyatv.querying.StdItemQuery;
import tv.emby.embyatv.startup.StartupActivity;
import tv.emby.embyatv.util.Utils;
/**
* Created by Eric on 3/1/2015.
*/
public class RecommendationManager {
private final String REC_FILE_NAME = "tv.mediabrowser.recommentations.json";
private final Integer MAX_TV_RECS = 3;
private final Integer MAX_MOVIE_RECS = 4;
private boolean isEnabled;
private static RecommendationManager instance;
private Recommendations mRecommendations;
public RecommendationManager() {
isEnabled = Build.VERSION.SDK_INT >= 21;
mRecommendations = loadRecs();
if (isEnabled) {
validate();
} else {
TvApp.getApplication().getLogger().Info("Recommendations not enabled on this device");
}
}
public static RecommendationManager getInstance() {
if (instance == null) instance = new RecommendationManager();
return instance;
}
public static void init() {
if (instance == null) {
getInstance();
} else {
instance.validate();
}
}
private Recommendations loadRecs() {
if (isEnabled) {
try {
InputStream recFile = TvApp.getApplication().openFileInput(REC_FILE_NAME);
String json = Utils.ReadStringFromFile(recFile);
recFile.close();
return (Recommendations) TvApp.getApplication().getSerializer().DeserializeFromString(json, Recommendations.class);
} catch (IOException e) {
// none saved
return new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
}
} else {
return new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
}
}
public boolean validate() {
if (isEnabled) {
//Now validate that these are for this server and user.
if (!mRecommendations.getServerId().equals(TvApp.getApplication().getApiClient().getServerInfo().getId())
|| !mRecommendations.getUserId().equals(TvApp.getApplication().getCurrentUser().getId())) {
//Nope - clear them out and start over for this user
clearAll();
createAll();
TvApp.getApplication().getLogger().Info("Recommendations re-set for user "+TvApp.getApplication().getCurrentUser().getName());
return false;
}
createAll();
TvApp.getApplication().getLogger().Info("Recommendations re-created for user "+TvApp.getApplication().getCurrentUser().getName());
}
return true;
}
private void saveRecs() {
if (isEnabled) {
try {
OutputStream recFile = TvApp.getApplication().openFileOutput(REC_FILE_NAME, Context.MODE_PRIVATE);
recFile.write(TvApp.getApplication().getSerializer().SerializeToString(mRecommendations).getBytes());
recFile.close();
} catch (IOException e) {
TvApp.getApplication().getLogger().ErrorException("Error saving recommendations",e);
}
}
}
public void clearAll() {
NotificationManager nm = (NotificationManager)
TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancelAll();
mRecommendations = new Recommendations(TvApp.getApplication().getApiClient().getServerInfo().getId(), TvApp.getApplication().getCurrentUser().getId());
saveRecs();
}
public void createAll() {
if (isEnabled) {
//Create recs for next up tv and movies
NextUpQuery nextUpQuery = new NextUpQuery();
nextUpQuery.setUserId(TvApp.getApplication().getCurrentUser().getId());
nextUpQuery.setLimit(MAX_TV_RECS);
nextUpQuery.setFields(new ItemFields[] {ItemFields.PrimaryImageAspectRatio});
TvApp.getApplication().getApiClient().GetNextUpEpisodesAsync(nextUpQuery, new Response<ItemsResult>() {
@Override
public void onResponse(ItemsResult response) {
if (response.getTotalRecordCount() > 0) {
for (BaseItemDto episode : response.getItems()) {
if (episode.getLocationType() != LocationType.Virtual) addRecommendation(episode, RecommendationType.Tv);
}
}
}
});
//First try for resumables
StdItemQuery resumeMovies = new StdItemQuery();
resumeMovies.setIncludeItemTypes(new String[]{"Movie"});
resumeMovies.setRecursive(true);
resumeMovies.setLimit(2);
resumeMovies.setFilters(new ItemFilter[]{ItemFilter.IsResumable});
resumeMovies.setSortBy(new String[]{ItemSortBy.DatePlayed});
resumeMovies.setSortOrder(SortOrder.Descending);
TvApp.getApplication().getApiClient().GetItemsAsync(resumeMovies, new Response<ItemsResult>() {
@Override
public void onResponse(ItemsResult response) {
int movieItems = 0;
if (response.getTotalRecordCount() > 0) {
for (BaseItemDto movie : response.getItems()) {
recommend(movie.getId());
movieItems++;
}
}
if (movieItems < MAX_MOVIE_RECS) {
//Now fill in with latest movies
StdItemQuery suggMovies = new StdItemQuery();
suggMovies.setIncludeItemTypes(new String[]{"Movie"});
suggMovies.setRecursive(true);
suggMovies.setLimit(MAX_MOVIE_RECS - movieItems);
suggMovies.setFilters(new ItemFilter[]{ItemFilter.IsUnplayed});
suggMovies.setSortBy(new String[]{ItemSortBy.DateCreated});
suggMovies.setSortOrder(SortOrder.Descending);
TvApp.getApplication().getApiClient().GetItemsAsync(suggMovies, new Response<ItemsResult>() {
@Override
public void onResponse(ItemsResult suggResponse) {
if (suggResponse.getTotalRecordCount() > 0) {
for (BaseItemDto movie : suggResponse.getItems()) {
addRecommendation(movie, RecommendationType.Movie);
}
}
}
});
}
}
});
}
}
public void recommend(final String itemId) {
if (isEnabled) {
if (itemId == null) {
TvApp.getApplication().getLogger().Error("Attempt to recommend null Item");
return;
}
//No matter what it is, if it is resumable, recommend this item (need to re-retrieve for current user data)
TvApp.getApplication().getApiClient().GetItemAsync(itemId, TvApp.getApplication().getCurrentUser().getId(), new Response<BaseItemDto>() {
@Override
public void onResponse(BaseItemDto response) {
if (response == null) {
TvApp.getApplication().getLogger().Error("No item found with ID: "+itemId);
return;
}
if (response.getCanResume()) {
addRecommendation(response, RecommendationType.Movie);
} else {
switch (response.getType()) {
case "Movie":
//First remove us if we were a recommendation
mRecommendations.remove(RecommendationType.Movie, response.getId());
//Suggest a similar movie
SimilarItemsQuery similar = new SimilarItemsQuery();
similar.setId(response.getId());
similar.setLimit(1);
similar.setUserId(TvApp.getApplication().getCurrentUser().getId());
TvApp.getApplication().getApiClient().GetSimilarItems(similar, new Response<ItemsResult>() {
@Override
public void onResponse(ItemsResult similarResponse) {
if (similarResponse.getTotalRecordCount() > 0) {
addRecommendation(similarResponse.getItems()[0], RecommendationType.Movie);
}
}
@Override
public void onError(Exception exception) {
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
}
});
break;
case "Episode":
//First remove us if we were a recommendation
mRecommendations.remove(RecommendationType.Tv, response.getId());
//Suggest the next up episode
NextUpQuery next = new NextUpQuery();
next.setSeriesId(response.getSeriesId());
next.setUserId(TvApp.getApplication().getCurrentUser().getId());
next.setLimit(1);
TvApp.getApplication().getApiClient().GetNextUpEpisodesAsync(next, new Response<ItemsResult>() {
@Override
public void onResponse(ItemsResult nextResponse) {
if (nextResponse.getTotalRecordCount() > 0 && nextResponse.getItems()[0].getLocationType() != LocationType.Virtual) {
addRecommendation(nextResponse.getItems()[0], RecommendationType.Tv);
}
}
@Override
public void onError(Exception exception) {
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
}
});
break;
}
}
}
@Override
public void onError(Exception exception) {
TvApp.getApplication().getLogger().ErrorException("Error retrieving item for recommendation", exception);
}
});
}
}
public boolean addRecommendation(BaseItemDto item, RecommendationType type) {
if (isEnabled) {
//No need if we are already there
if (mRecommendations.get(type, item.getId()) != null) return false;
//Not so build one
new AsyncRunner().execute(item, type);
return true;
} else {
return false;
}
}
private class AsyncRunner extends AsyncTask<Object, Integer, Boolean> {
@Override
protected Boolean doInBackground(Object... params) {
BaseItemDto item = (BaseItemDto) params[0];
RecommendationType type = (RecommendationType) params[1];
Recommendation rec = new Recommendation(type, item.getId());
rec.setRecId(mRecommendations.getRecId(type, type == RecommendationType.Movie ? MAX_MOVIE_RECS : MAX_TV_RECS));
RecommendationBuilder builder = new RecommendationBuilder()
.setContext(TvApp.getApplication())
.setSmallIcon(R.drawable.logoicon114);
Notification recommendation = builder
.setId(rec.getRecId())
.setPriority(0)
.setTitle(item.getName())
.setDescription(item.getOverview())
.setBitmap(Utils.getBitmapFromURL(Utils.getPrimaryImageUrl(item, TvApp.getApplication().getApiClient(), false, true, 300)))
.setBackground(Utils.getBackdropImageUrl(item, TvApp.getApplication().getApiClient(), true))
.setIntent(buildPendingIntent(item))
.build();
mRecommendations.add(rec);
saveRecs();
NotificationManager notificationManager = (NotificationManager)
TvApp.getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(rec.getRecId(), recommendation);
return true;
}
}
private PendingIntent buildPendingIntent(BaseItemDto item) {
Intent directIntent = new Intent(TvApp.getApplication(), StartupActivity.class);
directIntent.putExtra("ItemId", item.getId());
directIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(TvApp.getApplication());
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(directIntent);
// Ensure a unique PendingIntents, otherwise all recommendations end up with the same
// PendingIntent
directIntent.setAction(item.getId());
return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
}