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