1 package com.example.android.tv.channelsprograms.util; 2 3 import android.net.Uri; 4 import android.support.annotation.StringDef; 5 6 import java.util.List; 7 8 /** Builds and parses uris for deep linking within the app. */ 9 public class AppLinkHelper { 10 11 private static final String SCHEMA_URI_PREFIX = "tvrecommendation://app/"; 12 public static final String PLAYBACK = "playback"; 13 public static final String BROWSE = "browse"; 14 private static final String URI_PLAY = SCHEMA_URI_PREFIX + PLAYBACK; 15 private static final String URI_VIEW = SCHEMA_URI_PREFIX + BROWSE; 16 private static final int URI_INDEX_OPTION = 0; 17 private static final int URI_INDEX_CHANNEL = 1; 18 private static final int URI_INDEX_MOVIE = 2; 19 private static final int URI_INDEX_POSITION = 3; 20 public static final int DEFAULT_POSITION = -1; 21 22 /** 23 * Builds a {@link Uri} for deep link into playing a movie from the beginning. 24 * 25 * @param channelId - id of the channel the movie is in. 26 * @param movieId - id of the movie. 27 * @return a uri. 28 */ buildPlaybackUri(long channelId, long movieId)29 public static Uri buildPlaybackUri(long channelId, long movieId) { 30 return buildPlaybackUri(channelId, movieId, DEFAULT_POSITION); 31 } 32 33 /** 34 * Builds a {@link Uri} to deep link into continue playing a movie from a position. 35 * 36 * @param channelId - id of the channel the movie is in. 37 * @param movieId - id of the movie. 38 * @param position - position to continue playing. 39 * @return a uri. 40 */ buildPlaybackUri(long channelId, long movieId, long position)41 public static Uri buildPlaybackUri(long channelId, long movieId, long position) { 42 return Uri.parse(URI_PLAY) 43 .buildUpon() 44 .appendPath(String.valueOf(channelId)) 45 .appendPath(String.valueOf(movieId)) 46 .appendPath(String.valueOf(position)) 47 .build(); 48 } 49 50 /** 51 * Builds a {@link Uri} to deep link into viewing a subscription. 52 * 53 * @param subscriptionName - name of the subscription. 54 * @return a uri. 55 */ buildBrowseUri(String subscriptionName)56 public static Uri buildBrowseUri(String subscriptionName) { 57 return Uri.parse(URI_VIEW).buildUpon().appendPath(subscriptionName).build(); 58 } 59 60 /** 61 * Returns an {@link AppLinkAction} for the given Uri. 62 * 63 * @param uri to determine the intended action. 64 * @return an action. 65 */ extractAction(Uri uri)66 public static AppLinkAction extractAction(Uri uri) { 67 if (isPlaybackUri(uri)) { 68 return new PlaybackAction( 69 extractChannelId(uri), extractMovieId(uri), extractPosition(uri)); 70 } else if (isBrowseUri(uri)) { 71 return new BrowseAction(extractSubscriptionName(uri)); 72 } 73 throw new IllegalArgumentException("No action found for uri " + uri); 74 } 75 76 /** 77 * Tests if the {@link Uri} was built for playing a movie. 78 * 79 * @param uri to examine. 80 * @return true if the uri is for playing a movie. 81 */ isPlaybackUri(Uri uri)82 private static boolean isPlaybackUri(Uri uri) { 83 if (uri.getPathSegments().isEmpty()) { 84 return false; 85 } 86 String option = uri.getPathSegments().get(URI_INDEX_OPTION); 87 return PLAYBACK.equals(option); 88 } 89 90 /** 91 * Tests if a {@link Uri} was built for browsing a subscription. 92 * 93 * @param uri to examine. 94 * @return true if the Uri is for browsing a subscription. 95 */ isBrowseUri(Uri uri)96 private static boolean isBrowseUri(Uri uri) { 97 if (uri.getPathSegments().isEmpty()) { 98 return false; 99 } 100 String option = uri.getPathSegments().get(URI_INDEX_OPTION); 101 return BROWSE.equals(option); 102 } 103 104 /** 105 * Extracts the subscription name from the {@link Uri}. 106 * 107 * @param uri that contains a subscription name. 108 * @return the subscription name. 109 */ extractSubscriptionName(Uri uri)110 private static String extractSubscriptionName(Uri uri) { 111 return extract(uri, URI_INDEX_CHANNEL); 112 } 113 114 /** 115 * Extracts the channel id from the {@link Uri}. 116 * 117 * @param uri that contains a channel id. 118 * @return the channel id. 119 */ extractChannelId(Uri uri)120 private static long extractChannelId(Uri uri) { 121 return extractLong(uri, URI_INDEX_CHANNEL); 122 } 123 124 /** 125 * Extracts the movie id from the {@link Uri}. 126 * 127 * @param uri that contains a movie id. 128 * @return the movie id. 129 */ extractMovieId(Uri uri)130 private static long extractMovieId(Uri uri) { 131 return extractLong(uri, URI_INDEX_MOVIE); 132 } 133 134 /** 135 * Extracts the playback mPosition from the {@link Uri}. 136 * 137 * @param uri that contains a playback mPosition. 138 * @return the playback mPosition. 139 */ extractPosition(Uri uri)140 private static long extractPosition(Uri uri) { 141 return extractLong(uri, URI_INDEX_POSITION); 142 } 143 extractLong(Uri uri, int index)144 private static long extractLong(Uri uri, int index) { 145 return Long.valueOf(extract(uri, index)); 146 } 147 extract(Uri uri, int index)148 private static String extract(Uri uri, int index) { 149 List<String> pathSegments = uri.getPathSegments(); 150 if (pathSegments.isEmpty() || pathSegments.size() < index) { 151 return null; 152 } 153 return pathSegments.get(index); 154 } 155 156 @StringDef({BROWSE, PLAYBACK}) 157 public @interface ActionFlags {} 158 159 /** Action for deep linking. */ 160 public interface AppLinkAction { 161 /** Returns an string representation of the action. */ 162 @ActionFlags getAction()163 String getAction(); 164 } 165 166 /** Browse a subscription. */ 167 public static class BrowseAction implements AppLinkAction { 168 169 private final String mSubscriptionName; 170 BrowseAction(String subscriptionName)171 private BrowseAction(String subscriptionName) { 172 this.mSubscriptionName = subscriptionName; 173 } 174 getSubscriptionName()175 public String getSubscriptionName() { 176 return mSubscriptionName; 177 } 178 179 @Override getAction()180 public String getAction() { 181 return BROWSE; 182 } 183 } 184 185 /** Play a movie. */ 186 public static class PlaybackAction implements AppLinkAction { 187 188 private final long mChannelId; 189 private final long mMovieId; 190 private final long mPosition; 191 PlaybackAction(long channelId, long movieId, long position)192 private PlaybackAction(long channelId, long movieId, long position) { 193 this.mChannelId = channelId; 194 this.mMovieId = movieId; 195 this.mPosition = position; 196 } 197 getChannelId()198 public long getChannelId() { 199 return mChannelId; 200 } 201 getMovieId()202 public long getMovieId() { 203 return mMovieId; 204 } 205 getPosition()206 public long getPosition() { 207 return mPosition; 208 } 209 210 @Override getAction()211 public String getAction() { 212 return PLAYBACK; 213 } 214 } 215 } 216