1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.example.android.tv.channelsprograms.playback;
16 
17 import android.content.ContentUris;
18 import android.content.Context;
19 import android.net.Uri;
20 import android.support.annotation.NonNull;
21 import android.support.media.tv.TvContractCompat;
22 import android.support.media.tv.WatchNextProgram;
23 import android.util.Log;
24 
25 import com.example.android.tv.channelsprograms.model.MockDatabase;
26 import com.example.android.tv.channelsprograms.model.Movie;
27 import com.example.android.tv.channelsprograms.util.AppLinkHelper;
28 
29 /** Adds, updates, and removes the currently playing {@link Movie} from the "Watch Next" channel. */
30 public class WatchNextAdapter {
31 
32     private static final String TAG = "WatchNextAdapter";
33 
updateProgress( Context context, long channelId, Movie movie, long position, long duration)34     public void updateProgress(
35             Context context, long channelId, Movie movie, long position, long duration) {
36         Log.d(TAG, String.format("Updating the movie (%d) in watch next.", movie.getId()));
37 
38         Movie entity = MockDatabase.findMovieById(context, channelId, movie.getId());
39         if (entity == null) {
40             Log.e(
41                     TAG,
42                     String.format(
43                             "Could not find movie in channel: channel id: %d, movie id: %d",
44                             channelId, movie.getId()));
45             return;
46         }
47 
48         // TODO: step 12 add watch next program.
49         WatchNextProgram program = createWatchNextProgram(channelId, entity, position, duration);
50         if (entity.getWatchNextId() < 1L) {
51             // Create a program.
52             Uri watchNextProgramUri =
53                     context.getContentResolver()
54                             .insert(
55                                     TvContractCompat.WatchNextPrograms.CONTENT_URI,
56                                     program.toContentValues());
57             long watchNextId = ContentUris.parseId(watchNextProgramUri);
58             entity.setWatchNextId(watchNextId);
59             MockDatabase.saveMovie(context, channelId, entity);
60 
61             Log.d(TAG, "Watch Next program added: " + watchNextId);
62         } else {
63             // TODO: step 14 update program.
64             // Updates the progress and last engagement time of the program.
65             context.getContentResolver()
66                     .update(
67                             TvContractCompat.buildWatchNextProgramUri(entity.getWatchNextId()),
68                             program.toContentValues(),
69                             null,
70                             null);
71 
72             Log.d(TAG, "Watch Next program updated: " + entity.getWatchNextId());
73         }
74     }
75 
76     @NonNull
createWatchNextProgram( long channelId, Movie movie, long position, long duration)77     private WatchNextProgram createWatchNextProgram(
78             long channelId, Movie movie, long position, long duration) {
79         // TODO: step 13 convert movie
80         Uri posterArtUri = Uri.parse(movie.getCardImageUrl());
81         Uri intentUri = AppLinkHelper.buildPlaybackUri(channelId, movie.getId(), position);
82 
83         WatchNextProgram.Builder builder = new WatchNextProgram.Builder();
84         builder.setType(TvContractCompat.PreviewProgramColumns.TYPE_MOVIE)
85                 .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
86                 .setLastEngagementTimeUtcMillis(System.currentTimeMillis())
87                 .setLastPlaybackPositionMillis((int) position)
88                 .setDurationMillis((int) duration)
89                 .setTitle(movie.getTitle())
90                 .setDescription(movie.getDescription())
91                 .setPosterArtUri(posterArtUri)
92                 .setIntentUri(intentUri);
93         return builder.build();
94     }
95 
removeFromWatchNext(Context context, long channelId, long movieId)96     public void removeFromWatchNext(Context context, long channelId, long movieId) {
97         Movie movie = MockDatabase.findMovieById(context, channelId, movieId);
98         if (movie == null || movie.getWatchNextId() < 1L) {
99             Log.d(TAG, "No program to remove from watch next.");
100             return;
101         }
102 
103         // TODO: step 15 remove program
104         int rows =
105                 context.getContentResolver()
106                         .delete(
107                                 TvContractCompat.buildWatchNextProgramUri(movie.getWatchNextId()),
108                                 null,
109                                 null);
110         Log.d(TAG, String.format("Deleted %d programs(s) from watch next", rows));
111 
112         // Sync our records with the system; remove reference to watch next program.
113         movie.setWatchNextId(-1);
114         MockDatabase.saveMovie(context, channelId, movie);
115     }
116 }
117