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 17 package com.android.tv.settings.util; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent.ShortcutIconResource; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.content.res.Resources; 24 import android.graphics.drawable.Drawable; 25 import android.net.Uri; 26 import android.text.TextUtils; 27 28 /** 29 * Utilities for working with URIs. 30 */ 31 public final class UriUtils { 32 33 private static final String SCHEME_SHORTCUT_ICON_RESOURCE = "shortcut.icon.resource"; 34 private static final String SCHEME_DELIMITER = "://"; 35 private static final String URI_PATH_DELIMITER = "/"; 36 private static final String URI_PACKAGE_DELIMITER = ":"; 37 private static final String HTTP_PREFIX = "http"; 38 private static final String HTTPS_PREFIX = "https"; 39 private static final String SCHEME_ACCOUNT_IMAGE = "image.account"; 40 private static final String ACCOUNT_IMAGE_CHANGE_NOTIFY_URI = "change_notify_uri"; 41 private static final String DETAIL_DIALOG_URI_DIALOG_TITLE = "detail_dialog_title"; 42 private static final String DETAIL_DIALOG_URI_DIALOG_DESCRIPTION = "detail_dialog_description"; 43 private static final String DETAIL_DIALOG_URI_DIALOG_ACTION_START_INDEX = 44 "detail_dialog_action_start_index"; 45 private static final String DETAIL_DIALOG_URI_DIALOG_ACTION_START_NAME = 46 "detail_dialog_action_start_name"; 47 48 /** 49 * Non instantiable. 50 */ 51 private UriUtils() {} 52 53 /** 54 * get resource uri representation for a resource of a package 55 */ 56 public static String getAndroidResourceUri(Context context, int resourceId) { 57 return getAndroidResourceUri(context.getResources(), resourceId); 58 } 59 60 /** 61 * get resource uri representation for a resource 62 */ 63 public static String getAndroidResourceUri(Resources resources, int resourceId) { 64 return ContentResolver.SCHEME_ANDROID_RESOURCE 65 + SCHEME_DELIMITER + resources.getResourceName(resourceId) 66 .replace(URI_PACKAGE_DELIMITER, URI_PATH_DELIMITER); 67 } 68 69 /** 70 * load drawable from resource 71 * TODO: move to a separate class to handle bitmap and drawables 72 */ 73 public static Drawable getDrawable(Context context, ShortcutIconResource r) 74 throws NameNotFoundException { 75 Resources resources = context.getPackageManager().getResourcesForApplication(r.packageName); 76 if (resources == null) { 77 return null; 78 } 79 final int id = resources.getIdentifier(r.resourceName, null, null); 80 return resources.getDrawable(id); 81 } 82 83 /** 84 * Gets a URI with short cut icon scheme. 85 */ 86 public static Uri getShortcutIconResourceUri(ShortcutIconResource iconResource) { 87 return Uri.parse(SCHEME_SHORTCUT_ICON_RESOURCE + SCHEME_DELIMITER + iconResource.packageName 88 + URI_PATH_DELIMITER 89 + iconResource.resourceName.replace(URI_PACKAGE_DELIMITER, URI_PATH_DELIMITER)); 90 } 91 92 /** 93 * Gets a URI with scheme = {@link ContentResolver#SCHEME_ANDROID_RESOURCE}. 94 */ 95 public static Uri getAndroidResourceUri(String resourceName) { 96 Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + SCHEME_DELIMITER 97 + resourceName.replace(URI_PACKAGE_DELIMITER, URI_PATH_DELIMITER)); 98 return uri; 99 } 100 101 /** 102 * Checks if the URI refers to an Android resource. 103 */ 104 public static boolean isAndroidResourceUri(Uri uri) { 105 return ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme()); 106 } 107 108 /** 109 * Gets a URI with the account image scheme. 110 */ 111 public static Uri getAccountImageUri(String accountName) { 112 Uri uri = Uri.parse(SCHEME_ACCOUNT_IMAGE + SCHEME_DELIMITER + accountName); 113 return uri; 114 } 115 116 /** 117 * Gets a URI with the account image scheme, and specifying an URI to be 118 * used in notifyChange() when the image pointed to by the returned URI is 119 * updated. 120 */ 121 public static Uri getAccountImageUri(String accountName, Uri changeNotifyUri) { 122 Uri uri = Uri.parse(SCHEME_ACCOUNT_IMAGE + SCHEME_DELIMITER + accountName); 123 if (changeNotifyUri != null) { 124 uri = uri.buildUpon().appendQueryParameter(ACCOUNT_IMAGE_CHANGE_NOTIFY_URI, 125 changeNotifyUri.toString()).build(); 126 } 127 return uri; 128 } 129 130 /** 131 * Checks if the URI refers to an account image. 132 */ 133 public static boolean isAccountImageUri(Uri uri) { 134 return uri == null ? false : SCHEME_ACCOUNT_IMAGE.equals(uri.getScheme()); 135 } 136 137 public static String getAccountName(Uri uri) { 138 if (isAccountImageUri(uri)) { 139 String accountName = uri.getAuthority() + uri.getPath(); 140 return accountName; 141 } else { 142 throw new IllegalArgumentException("Invalid account image URI. " + uri); 143 } 144 } 145 146 public static Uri getAccountImageChangeNotifyUri(Uri uri) { 147 if (isAccountImageUri(uri)) { 148 String notifyUri = uri.getQueryParameter(ACCOUNT_IMAGE_CHANGE_NOTIFY_URI); 149 if (notifyUri == null) { 150 return null; 151 } else { 152 return Uri.parse(notifyUri); 153 } 154 } else { 155 throw new IllegalArgumentException("Invalid account image URI. " + uri); 156 } 157 } 158 159 /** 160 * Returns {@code true} if the URI refers to a content URI which can be opened via 161 * {@link ContentResolver#openInputStream(Uri)}. 162 */ 163 public static boolean isContentUri(Uri uri) { 164 return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) || 165 ContentResolver.SCHEME_FILE.equals(uri.getScheme()); 166 } 167 168 /** 169 * Checks if the URI refers to an shortcut icon resource. 170 */ 171 public static boolean isShortcutIconResourceUri(Uri uri) { 172 return SCHEME_SHORTCUT_ICON_RESOURCE.equals(uri.getScheme()); 173 } 174 175 /** 176 * Creates a shortcut icon resource object from an Android resource URI. 177 */ 178 public static ShortcutIconResource getIconResource(Uri uri) { 179 if(isAndroidResourceUri(uri)) { 180 ShortcutIconResource iconResource = new ShortcutIconResource(); 181 iconResource.packageName = uri.getAuthority(); 182 // Trim off the scheme + 3 extra for "://", then replace the first "/" with a ":" 183 iconResource.resourceName = uri.toString().substring( 184 ContentResolver.SCHEME_ANDROID_RESOURCE.length() + SCHEME_DELIMITER.length()) 185 .replaceFirst(URI_PATH_DELIMITER, URI_PACKAGE_DELIMITER); 186 return iconResource; 187 } else if(isShortcutIconResourceUri(uri)) { 188 ShortcutIconResource iconResource = new ShortcutIconResource(); 189 iconResource.packageName = uri.getAuthority(); 190 iconResource.resourceName = uri.toString().substring( 191 SCHEME_SHORTCUT_ICON_RESOURCE.length() + SCHEME_DELIMITER.length() 192 + iconResource.packageName.length() + URI_PATH_DELIMITER.length()) 193 .replaceFirst(URI_PATH_DELIMITER, URI_PACKAGE_DELIMITER); 194 return iconResource; 195 } else { 196 throw new IllegalArgumentException("Invalid resource URI. " + uri); 197 } 198 } 199 200 /** 201 * Returns {@code true} if this is a web URI. 202 */ 203 public static boolean isWebUri(Uri resourceUri) { 204 String scheme = resourceUri.getScheme() == null ? null 205 : resourceUri.getScheme().toLowerCase(); 206 return HTTP_PREFIX.equals(scheme) || HTTPS_PREFIX.equals(scheme); 207 } 208 209 /** 210 * Build a Uri for canvas details subactions dialog given content uri and optional parameters. 211 * @param uri the subactions ContentUri 212 * @param dialogTitle the custom subactions dialog title. If the value is null, canvas will 213 * fall back to use previous action's name as the subactions dialog title. 214 * @param dialogDescription the custom subactions dialog description. If the value is null, 215 * canvas will fall back to use previous action's subname as the subactions dialog 216 * description. 217 * @return 218 */ 219 public static Uri getSubactionDialogUri(Uri uri, String dialogTitle, String dialogDescription) { 220 return getSubactionDialogUri(uri, dialogTitle, dialogDescription, null, -1); 221 } 222 223 /** 224 * Build a Uri for canvas details subactions dialog given content uri and optional parameters. 225 * @param uri the subactions ContentUri 226 * @param dialogTitle the custom subactions dialog title. If the value is null, canvas will 227 * fall back to use previous action's name as the subactions dialog title. 228 * @param dialogDescription the custom subactions dialog description. If the value is null, 229 * canvas will fall back to use previous action's subname as the subactions dialog 230 * description. 231 * @param startIndex the focused action in actions list when started. 232 * @return 233 */ 234 public static Uri getSubactionDialogUri(Uri uri, String dialogTitle, String dialogDescription, 235 int startIndex) { 236 return getSubactionDialogUri(uri, dialogTitle, dialogDescription, null, startIndex); 237 } 238 239 /** 240 * Build a Uri for canvas details subactions dialog given content uri and optional parameters. 241 * @param uri the subactions ContentUri 242 * @param dialogTitle the custom subactions dialog title. If the value is null, canvas will 243 * fall back to use previous action's name as the subactions dialog title. 244 * @param dialogDescription the custom subactions dialog description. If the value is null, 245 * canvas will fall back to use previous action's subname as the subactions dialog 246 * description. 247 * @param startName the name of action that is focused in actions list when started. 248 * @return 249 */ 250 public static Uri getSubactionDialogUri(Uri uri, String dialogTitle, String dialogDescription, 251 String startName) { 252 return getSubactionDialogUri(uri, dialogTitle, dialogDescription, startName, -1); 253 } 254 255 /** 256 * Build a Uri for canvas details subactions dialog given content uri and optional parameters. 257 * @param uri the subactions ContentUri 258 * @param dialogTitle the custom subactions dialog title. If the value is null, canvas will 259 * fall back to use previous action's name as the subactions dialog title. 260 * @param dialogDescription the custom subactions dialog description. If the value is null, 261 * canvas will fall back to use previous action's subname as the subactions dialog 262 * description. 263 * @param startIndex the focused action in actions list when started. 264 * @param startName the name of action that is focused in actions list when started. startName 265 * takes priority over start index. 266 * @return 267 */ 268 public static Uri getSubactionDialogUri(Uri uri, String dialogTitle, String dialogDescription, 269 String startName, int startIndex) { 270 if (uri == null || !isContentUri(uri)) { 271 // If given uri is null, or it is not of contentUri type, return null. 272 return null; 273 } 274 275 Uri.Builder builder = uri.buildUpon(); 276 if (!TextUtils.isEmpty(dialogTitle)) { 277 builder.appendQueryParameter(DETAIL_DIALOG_URI_DIALOG_TITLE, dialogTitle); 278 } 279 280 if (!TextUtils.isEmpty(DETAIL_DIALOG_URI_DIALOG_DESCRIPTION)) { 281 builder.appendQueryParameter(DETAIL_DIALOG_URI_DIALOG_DESCRIPTION, dialogDescription); 282 } 283 284 if (startIndex != -1) { 285 builder.appendQueryParameter(DETAIL_DIALOG_URI_DIALOG_ACTION_START_INDEX, 286 Integer.toString(startIndex)); 287 } 288 289 if (!TextUtils.isEmpty(startName)) { 290 builder.appendQueryParameter(DETAIL_DIALOG_URI_DIALOG_ACTION_START_NAME, startName); 291 } 292 293 return builder.build(); 294 } 295 296 /** 297 * Get subaction dialog title parameter from URI 298 * @param uri ContentUri for canvas details subactions 299 * @return custom dialog title if this parameter is available in URI. Otherwise, return null. 300 */ 301 public static String getSubactionDialogTitle(Uri uri) { 302 if (uri == null || !isContentUri(uri)) { 303 return null; 304 } 305 306 return uri.getQueryParameter(DETAIL_DIALOG_URI_DIALOG_TITLE); 307 } 308 309 /** 310 * Get subaction dialog description parameter from URI 311 * @param uri ContentUri for canvas details subactions 312 * @return custom dialog description if this parameter is available in URI. 313 * Otherwise, return null. 314 */ 315 public static String getSubactionDialogDescription(Uri uri) { 316 if (uri == null || !isContentUri(uri)) { 317 return null; 318 } 319 320 return uri.getQueryParameter(DETAIL_DIALOG_URI_DIALOG_DESCRIPTION); 321 } 322 323 /** 324 * Get subaction dialog action list focused index when started from URI 325 * @param uri ContentUri for canvas details subactions 326 * @return action starting index if this parameter is available in URI. Otherwise, return -1. 327 */ 328 public static int getSubactionDialogActionStartIndex(Uri uri) { 329 if (uri == null || !isContentUri(uri)) { 330 return -1; 331 } 332 333 String startIndexStr = uri.getQueryParameter(DETAIL_DIALOG_URI_DIALOG_ACTION_START_INDEX); 334 if (!TextUtils.isEmpty(startIndexStr) && TextUtils.isDigitsOnly(startIndexStr)) { 335 return Integer.parseInt(startIndexStr); 336 } else { 337 return -1; 338 } 339 } 340 341 /** 342 * Get subaction dialog action list focused action name when started from URI 343 * @param uri ContentUri for canvas details subactions 344 * @return that name of starting action if this parameter is available in URI. 345 * Otherwise, return null. 346 */ 347 public static String getSubactionDialogActionStartName(Uri uri) { 348 if (uri == null || !isContentUri(uri)) { 349 return null; 350 } 351 352 return uri.getQueryParameter(DETAIL_DIALOG_URI_DIALOG_ACTION_START_NAME); 353 } 354 } 355