Home | History | Annotate | Download | only in settingslib
      1 /*
      2  * Copyright (C) 2012 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.settingslib;
     18 
     19 import android.app.Activity;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.content.res.Resources;
     27 import android.content.res.TypedArray;
     28 import android.net.Uri;
     29 import android.provider.Settings.Global;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 import android.view.Menu;
     33 import android.view.MenuItem;
     34 import android.view.MenuItem.OnMenuItemClickListener;
     35 
     36 import java.net.URISyntaxException;
     37 import java.util.Locale;
     38 
     39 import com.android.settingslib.helputils.R;
     40 
     41 /**
     42  * Functions to easily prepare contextual help menu option items with an intent that opens up the
     43  * browser to a particular URL, while taking into account the preferred language and app version.
     44  */
     45 public class HelpUtils {
     46     private final static String TAG = HelpUtils.class.getSimpleName();
     47 
     48     private static final int MENU_HELP = Menu.FIRST + 100;
     49 
     50     /**
     51      * Help URL query parameter key for the preferred language.
     52      */
     53     private final static String PARAM_LANGUAGE_CODE = "hl";
     54 
     55     /**
     56      * Help URL query parameter key for the app version.
     57      */
     58     private final static String PARAM_VERSION = "version";
     59 
     60     // Constants for help intents.
     61     private static final String EXTRA_CONTEXT = "EXTRA_CONTEXT";
     62     private static final String EXTRA_THEME = "EXTRA_THEME";
     63     private static final String EXTRA_PRIMARY_COLOR = "EXTRA_PRIMARY_COLOR";
     64     private static final String EXTRA_BACKUP_URI = "EXTRA_BACKUP_URI";
     65 
     66     /**
     67      * Cached version code to prevent repeated calls to the package manager.
     68      */
     69     private static String sCachedVersionCode = null;
     70 
     71     /** Static helper that is not instantiable*/
     72     private HelpUtils() { }
     73 
     74     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
     75             String backupContext) {
     76         MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
     77         helpItem.setIcon(R.drawable.ic_help_actionbar);
     78         return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
     79     }
     80 
     81     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
     82             String backupContext) {
     83         MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
     84         helpItem.setIcon(R.drawable.ic_help_actionbar);
     85         return prepareHelpMenuItem(activity, helpItem, activity.getString(helpUriResource),
     86                 backupContext);
     87     }
     88 
     89     /**
     90      * Prepares the help menu item by doing the following.
     91      * - If the helpUrlString is empty or null, the help menu item is made invisible.
     92      * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
     93      *   item to view the URL.
     94      *
     95      * @return returns whether the help menu item has been made visible.
     96      */
     97     public static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
     98             String helpUriString, String backupContext) {
     99         if (Global.getInt(activity.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
    100             return false;
    101         }
    102         if (TextUtils.isEmpty(helpUriString)) {
    103             // The help url string is empty or null, so set the help menu item to be invisible.
    104             helpMenuItem.setVisible(false);
    105 
    106             // return that the help menu item is not visible (i.e. false)
    107             return false;
    108         } else {
    109             final Intent intent = getHelpIntent(activity, helpUriString, backupContext);
    110 
    111             // Set the intent to the help menu item, show the help menu item in the overflow
    112             // menu, and make it visible.
    113             if (intent != null) {
    114                 helpMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
    115                     @Override
    116                     public boolean onMenuItemClick(MenuItem item) {
    117                         /**
    118                          * TODO: Enable metrics logger for @SystemApi (b/111552654)
    119                          *
    120                         MetricsLogger.action(activity,
    121                             MetricsEvent.ACTION_SETTING_HELP_AND_FEEDBACK,
    122                             intent.getStringExtra(EXTRA_CONTEXT));
    123                         */
    124                         try {
    125                             activity.startActivityForResult(intent, 0);
    126                         } catch (ActivityNotFoundException exc) {
    127                             Log.e(TAG, "No activity found for intent: " + intent);
    128                         }
    129                         return true;
    130                     }
    131                 });
    132                 helpMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    133                 helpMenuItem.setVisible(true);
    134             } else {
    135                 helpMenuItem.setVisible(false);
    136                 return false;
    137             }
    138 
    139             // return that the help menu item is visible (i.e., true)
    140             return true;
    141         }
    142     }
    143 
    144     public static Intent getHelpIntent(Context context, String helpUriString,
    145             String backupContext) {
    146         if (Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
    147             return null;
    148         }
    149         // Try to handle as Intent Uri, otherwise just treat as Uri.
    150         try {
    151             Intent intent = Intent.parseUri(helpUriString,
    152                     Intent.URI_ANDROID_APP_SCHEME | Intent.URI_INTENT_SCHEME);
    153             addIntentParameters(context, intent, backupContext, true /* sendPackageName */);
    154             ComponentName component = intent.resolveActivity(context.getPackageManager());
    155             if (component != null) {
    156                 return intent;
    157             } else if (intent.hasExtra(EXTRA_BACKUP_URI)) {
    158                 // This extra contains a backup URI for when the intent isn't available.
    159                 return getHelpIntent(context, intent.getStringExtra(EXTRA_BACKUP_URI),
    160                         backupContext);
    161             } else {
    162                 return null;
    163             }
    164         } catch (URISyntaxException e) {
    165         }
    166         // The help url string exists, so first add in some extra query parameters.
    167         final Uri fullUri = uriWithAddedParameters(context, Uri.parse(helpUriString));
    168 
    169         // Then, create an intent that will be fired when the user
    170         // selects this help menu item.
    171         Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
    172         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    173                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    174         return intent;
    175     }
    176 
    177     public static void addIntentParameters(Context context, Intent intent, String backupContext,
    178             boolean sendPackageName) {
    179         if (!intent.hasExtra(EXTRA_CONTEXT)) {
    180             // Insert some context if none exists.
    181             intent.putExtra(EXTRA_CONTEXT, backupContext);
    182         }
    183 
    184         Resources resources = context.getResources();
    185         boolean includePackageName =
    186                 resources.getBoolean(android.R.bool.config_sendPackageName);
    187 
    188         if (sendPackageName && includePackageName) {
    189             String[] packageNameKey =
    190                     {resources.getString(android.R.string.config_helpPackageNameKey)};
    191             String[] packageNameValue =
    192                     {resources.getString(android.R.string.config_helpPackageNameValue)};
    193             String helpIntentExtraKey =
    194                     resources.getString(android.R.string.config_helpIntentExtraKey);
    195             String helpIntentNameKey =
    196                     resources.getString(android.R.string.config_helpIntentNameKey);
    197             String feedbackIntentExtraKey =
    198                     resources.getString(android.R.string.config_feedbackIntentExtraKey);
    199             String feedbackIntentNameKey =
    200                     resources.getString(android.R.string.config_feedbackIntentNameKey);
    201             intent.putExtra(helpIntentExtraKey, packageNameKey);
    202             intent.putExtra(helpIntentNameKey, packageNameValue);
    203             intent.putExtra(feedbackIntentExtraKey, packageNameKey);
    204             intent.putExtra(feedbackIntentNameKey, packageNameValue);
    205         }
    206         intent.putExtra(EXTRA_THEME, 0 /* Light theme */);
    207         TypedArray array = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
    208         intent.putExtra(EXTRA_PRIMARY_COLOR, array.getColor(0, 0));
    209         array.recycle();
    210     }
    211 
    212     /**
    213      * Adds two query parameters into the Uri, namely the language code and the version code
    214      * of the app's package as gotten via the context.
    215      * @return the uri with added query parameters
    216      */
    217     private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
    218         Uri.Builder builder = baseUri.buildUpon();
    219 
    220         // Add in the preferred language
    221         builder.appendQueryParameter(PARAM_LANGUAGE_CODE, Locale.getDefault().toString());
    222 
    223         // Add in the package version code
    224         if (sCachedVersionCode == null) {
    225             // There is no cached version code, so try to get it from the package manager.
    226             try {
    227                 // cache the version code
    228                 PackageInfo info = context.getPackageManager().getPackageInfo(
    229                         context.getPackageName(), 0);
    230                 sCachedVersionCode = Long.toString(info.getLongVersionCode());
    231 
    232                 // append the version code to the uri
    233                 builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
    234             } catch (NameNotFoundException e) {
    235                 // Cannot find the package name, so don't add in the version parameter
    236                 // This shouldn't happen.
    237                 Log.wtf(TAG, "Invalid package name for context", e);
    238             }
    239         } else {
    240             builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
    241         }
    242 
    243         // Build the full uri and return it
    244         return builder.build();
    245     }
    246 }
    247