Utils.java revision 91033e05806d697a70bfee6140d394f9c6c98841
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.tv.util; 18 19import android.content.ContentUris; 20import android.content.ContentValues; 21import android.content.Context; 22import android.content.Intent; 23import android.content.SharedPreferences; 24import android.content.pm.ActivityInfo; 25import android.content.pm.PackageManager; 26import android.content.pm.ResolveInfo; 27import android.content.pm.ServiceInfo; 28import android.database.Cursor; 29import android.media.tv.TvContract; 30import android.media.tv.TvInputInfo; 31import android.net.Uri; 32import android.preference.PreferenceManager; 33import android.text.TextUtils; 34import android.util.Base64; 35 36import com.android.tv.data.Channel; 37import com.android.tv.data.Program; 38import com.android.tv.data.StreamInfo; 39 40import java.util.List; 41 42/** 43 * A class that includes convenience methods for accessing TvProvider database. 44 */ 45public class Utils { 46 public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; 47 public static final String EXTRA_SERVICE_NAME = "serviceName"; 48 public static final String EXTRA_KEYCODE = "keycode"; 49 50 public static final String CHANNEL_SORT_ORDER_BY_DISPLAY_NUMBER = 51 "CAST(" + TvContract.Channels.COLUMN_DISPLAY_NUMBER + " AS INTEGER), " 52 + "CAST(SUBSTR(LTRIM(" + TvContract.Channels.COLUMN_DISPLAY_NUMBER 53 + ",'0123456789'),2) AS INTEGER)"; 54 55 // preferences stored in the default preference. 56 private static final String PREF_KEY_LAST_SELECTED_TV_INPUT = "last_selected_tv_input"; 57 private static final String PREF_KEY_LAST_SELECTED_PHYS_TV_INPUT = 58 "last_selected_phys_tv_input"; 59 60 private static final String PREFIX_PREF_NAME = "com.android.tv."; 61 // preferences stored in the preference of a specific tv input. 62 private static final String PREF_KEY_LAST_WATCHED_CHANNEL_ID = "last_watched_channel_id"; 63 64 // STOPSHIP: Use the one defined in the contract class instead. 65 private static final String TvContract_Programs_COLUMN_VIDEO_RESOLUTION = "video_resolution"; 66 67 private static int VIDEO_SD_WIDTH = 704; 68 private static int VIDEO_SD_HEIGHT = 480; 69 private static int VIDEO_HD_WIDTH = 1280; 70 private static int VIDEO_HD_HEIGHT = 720; 71 private static int VIDEO_FULL_HD_WIDTH = 1920; 72 private static int VIDEO_FULL_HD_HEIGHT = 1080; 73 private static int VIDEO_ULTRA_HD_WIDTH = 2048; 74 private static int VIDEO_ULTRA_HD_HEIGHT = 1536; 75 76 private enum AspectRatio { 77 ASPECT_RATIO_4_3(4, 3), 78 ASPECT_RATIO_16_9(16, 9), 79 ASPECT_RATIO_21_9(21, 9); 80 81 final int width; 82 final int height; 83 84 AspectRatio(int width, int height) { 85 this.width = width; 86 this.height = height; 87 } 88 89 @Override 90 public String toString() { 91 return String.format("%d:%d", width, height); 92 } 93 } 94 95 private Utils() { /* cannot be instantiated */ } 96 97 public static Uri getChannelUri(long channelId) { 98 return ContentUris.withAppendedId(TvContract.Channels.CONTENT_URI, channelId); 99 } 100 101 public static String getInputIdForChannel(Context context, long channelId) { 102 if (channelId == Channel.INVALID_ID) { 103 return null; 104 } 105 Uri channelUri = ContentUris.withAppendedId(TvContract.Channels.CONTENT_URI, channelId); 106 return getInputIdForChannel(context, channelUri); 107 } 108 109 public static String getInputIdForChannel(Context context, Uri channelUri) { 110 if (channelUri == null) { 111 return null; 112 } 113 String[] projection = { TvContract.Channels.COLUMN_INPUT_ID }; 114 Cursor cursor = null; 115 try { 116 cursor = context.getContentResolver().query(channelUri, projection, null, null, null); 117 if (cursor != null && cursor.moveToNext()) { 118 return cursor.getString(0); 119 } 120 } finally { 121 if (cursor != null) { 122 cursor.close(); 123 } 124 } 125 return null; 126 } 127 128 public static void setLastWatchedChannelId(Context context, String inputId, String physInputId, 129 long channelId) { 130 if (TextUtils.isEmpty(inputId)) { 131 throw new IllegalArgumentException("inputId cannot be empty"); 132 } 133 context.getSharedPreferences(getPreferenceName(inputId), Context.MODE_PRIVATE).edit() 134 .putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, channelId).apply(); 135 PreferenceManager.getDefaultSharedPreferences(context).edit() 136 .putString(PREF_KEY_LAST_SELECTED_TV_INPUT, inputId).apply(); 137 PreferenceManager.getDefaultSharedPreferences(context).edit() 138 .putString(PREF_KEY_LAST_SELECTED_PHYS_TV_INPUT, physInputId).apply(); 139 } 140 141 public static long getLastWatchedChannelId(Context context) { 142 String inputId = getLastSelectedInputId(context); 143 if (inputId == null) { 144 return Channel.INVALID_ID; 145 } 146 return getLastWatchedChannelId(context, inputId); 147 } 148 149 public static long getLastWatchedChannelId(Context context, String inputId) { 150 if (TextUtils.isEmpty(inputId)) { 151 throw new IllegalArgumentException("inputId cannot be empty"); 152 } 153 return context.getSharedPreferences(getPreferenceName(inputId), 154 Context.MODE_PRIVATE).getLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, Channel.INVALID_ID); 155 } 156 157 public static String getLastSelectedInputId(Context context) { 158 return PreferenceManager.getDefaultSharedPreferences(context) 159 .getString(PREF_KEY_LAST_SELECTED_TV_INPUT, null); 160 } 161 162 public static String getLastSelectedPhysInputId(Context context) { 163 return PreferenceManager.getDefaultSharedPreferences(context) 164 .getString(PREF_KEY_LAST_SELECTED_PHYS_TV_INPUT, null); 165 } 166 167 public static Program getCurrentProgram(Context context, Uri channelUri) { 168 if (channelUri == null) { 169 return null; 170 } 171 long time = System.currentTimeMillis(); 172 Uri uri = TvContract.buildProgramsUriForChannel(channelUri, time, time); 173 String[] projection = { 174 TvContract.Programs.COLUMN_TITLE, 175 TvContract.Programs.COLUMN_SHORT_DESCRIPTION, 176 TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 177 TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, 178 TvContract_Programs_COLUMN_VIDEO_RESOLUTION, 179 TvContract.Programs.COLUMN_POSTER_ART_URI, 180 TvContract.Programs.COLUMN_THUMBNAIL_URI }; 181 Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); 182 String title = null; 183 String description = null; 184 long startTime = -1; 185 long endTime = -1; 186 String posterArtUri = null; 187 String thumbnailUri = null; 188 String videoDefinitionLevel = ""; 189 if (cursor.moveToNext()) { 190 title = cursor.getString(0); 191 description = cursor.getString(1); 192 startTime = cursor.getLong(2); 193 endTime = cursor.getLong(3); 194 videoDefinitionLevel = cursor.getString(4); 195 posterArtUri = cursor.getString(5); 196 thumbnailUri = cursor.getString(6); 197 } 198 cursor.close(); 199 200 // TODO: Consider providing the entire data if needed. 201 return new Program.Builder() 202 .setTitle(title) 203 .setDescription(description) 204 .setStartTimeUtcMillis(startTime) 205 .setEndTimeUtcMillis(endTime) 206 .setVideoDefinitionLevel(videoDefinitionLevel) 207 .setPosterArtUri(posterArtUri) 208 .setThumbnailUri(thumbnailUri).build(); 209 } 210 211 public static void updateCurrentVideoResolution(Context context, Long channelId, int format) { 212 if (channelId == Channel.INVALID_ID) { 213 return; 214 } 215 Uri channelUri = TvContract.buildChannelUri(channelId); 216 long time = System.currentTimeMillis(); 217 Uri uri = TvContract.buildProgramsUriForChannel(channelUri, time, time); 218 Cursor cursor = null; 219 String[] projection = { TvContract.Programs._ID }; 220 long programId = Program.INVALID_ID; 221 try { 222 cursor = context.getContentResolver().query(uri, projection, null, null, null); 223 if (cursor == null || !cursor.moveToNext()) { 224 return; 225 } 226 programId = cursor.getLong(0); 227 } finally { 228 if (cursor != null) { 229 cursor.close(); 230 } 231 } 232 233 String videoResolution = getVideoDefinitionLevelString(format); 234 if (TextUtils.isEmpty(videoResolution)) { 235 return; 236 } 237 Uri programUri = TvContract.buildProgramUri(programId); 238 ContentValues values = new ContentValues(); 239 values.put(TvContract_Programs_COLUMN_VIDEO_RESOLUTION, videoResolution); 240 context.getContentResolver().update(programUri, values, null, null); 241 } 242 243 public static boolean hasChannel(Context context, TvInputInfo name) { 244 return hasChannel(context, name, true); 245 } 246 247 public static boolean hasChannel(Context context, TvInputInfo name, boolean browsableOnly) { 248 Uri uri = TvContract.buildChannelsUriForInput(name.getId(), browsableOnly); 249 String[] projection = { TvContract.Channels._ID }; 250 Cursor cursor = null; 251 try { 252 cursor = context.getContentResolver().query(uri, projection, null, null, null); 253 return cursor != null && cursor.getCount() > 0; 254 } finally { 255 if (cursor != null) { 256 cursor.close(); 257 } 258 } 259 } 260 261 public static SharedPreferences getSharedPreferencesOfDisplayNameForInput(Context context) { 262 return context.getSharedPreferences(TvSettings.PREFS_FILE, Context.MODE_PRIVATE); 263 } 264 265 public static String getDisplayNameForInput(Context context, TvInputInfo info) { 266 SharedPreferences preferences = getSharedPreferencesOfDisplayNameForInput(context); 267 return preferences.getString(TvSettings.PREF_DISPLAY_INPUT_NAME + info.getId(), 268 info.loadLabel(context).toString()); 269 } 270 271 public static boolean hasActivity(Context context, TvInputInfo input, String action) { 272 return getActivityInfo(context, input, action) != null; 273 } 274 275 public static String getAspectRatioString(int width, int height) { 276 if (width == 0 || height == 0) { 277 return ""; 278 } 279 280 for (AspectRatio ratio: AspectRatio.values()) { 281 if (Math.abs((float) ratio.height / ratio.width - (float) height / width) < 0.05f) { 282 return ratio.toString(); 283 } 284 } 285 return ""; 286 } 287 288 public static int getVideoDefinitionLevelFromSize(int width, int height) { 289 if (width >= VIDEO_ULTRA_HD_WIDTH && height >= VIDEO_ULTRA_HD_HEIGHT) { 290 return StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD; 291 } else if (width >= VIDEO_FULL_HD_WIDTH && height >= VIDEO_FULL_HD_HEIGHT) { 292 return StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD; 293 } else if (width >= VIDEO_HD_WIDTH && height >= VIDEO_HD_HEIGHT) { 294 return StreamInfo.VIDEO_DEFINITION_LEVEL_HD; 295 } else if (width >= VIDEO_SD_WIDTH && height >= VIDEO_SD_HEIGHT) { 296 return StreamInfo.VIDEO_DEFINITION_LEVEL_SD; 297 } 298 return StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN; 299 } 300 301 public static String getVideoDefinitionLevelString(int videoFormat) { 302 switch (videoFormat) { 303 case StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD: 304 return "Ultra HD"; 305 case StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD: 306 return "Full HD"; 307 case StreamInfo.VIDEO_DEFINITION_LEVEL_HD: 308 return "HD"; 309 case StreamInfo.VIDEO_DEFINITION_LEVEL_SD: 310 return "SD"; 311 } 312 return ""; 313 } 314 315 public static String getAudioChannelString(int channelCount) { 316 switch (channelCount) { 317 case 1: 318 return "MONO"; 319 case 2: 320 return "STEREO"; 321 case 6: 322 return "5.1"; 323 case 8: 324 return "7.1"; 325 } 326 return ""; 327 } 328 329 private static ActivityInfo getActivityInfo(Context context, TvInputInfo input, String action) { 330 if (input == null) { 331 return null; 332 } 333 334 List<ResolveInfo> infos = context.getPackageManager().queryIntentActivities( 335 new Intent(action), PackageManager.GET_ACTIVITIES); 336 if (infos == null) { 337 return null; 338 } 339 340 for (ResolveInfo info : infos) { 341 if (info.activityInfo.packageName.equals(input.getServiceInfo().packageName)) { 342 return info.activityInfo; 343 } 344 } 345 return null; 346 } 347 348 private static String getPreferenceName(String inputId) { 349 return PREFIX_PREF_NAME + Base64.encodeToString(inputId.getBytes(), Base64.URL_SAFE); 350 } 351} 352