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.Resources.Theme;
     28 import android.content.res.TypedArray;
     29 import android.net.Uri;
     30 import android.provider.Settings.Global;
     31 import android.text.TextUtils;
     32 import android.util.Log;
     33 import android.util.TypedValue;
     34 import android.view.Menu;
     35 import android.view.MenuItem;
     36 import android.view.MenuItem.OnMenuItemClickListener;
     37 import com.android.internal.logging.MetricsLogger;
     38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     39 
     40 import java.net.URISyntaxException;
     41 import java.util.Locale;
     42 
     43 /**
     44  * Functions to easily prepare contextual help menu option items with an intent that opens up the
     45  * browser to a particular URL, while taking into account the preferred language and app version.
     46  */
     47 public class HelpUtils {
     48     private final static String TAG = HelpUtils.class.getSimpleName();
     49 
     50     private static final int MENU_HELP = Menu.FIRST + 100;
     51 
     52     /**
     53      * Help URL query parameter key for the preferred language.
     54      */
     55     private final static String PARAM_LANGUAGE_CODE = "hl";
     56 
     57     /**
     58      * Help URL query parameter key for the app version.
     59      */
     60     private final static String PARAM_VERSION = "version";
     61 
     62     // Constants for help intents.
     63     private static final String EXTRA_CONTEXT = "EXTRA_CONTEXT";
     64     private static final String EXTRA_THEME = "EXTRA_THEME";
     65     private static final String EXTRA_PRIMARY_COLOR = "EXTRA_PRIMARY_COLOR";
     66     private static final String EXTRA_BACKUP_URI = "EXTRA_BACKUP_URI";
     67 
     68     /**
     69      * Cached version code to prevent repeated calls to the package manager.
     70      */
     71     private static String sCachedVersionCode = null;
     72 
     73     /** Static helper that is not instantiable*/
     74     private HelpUtils() { }
     75 
     76     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
     77             String backupContext) {
     78         MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
     79         helpItem.setIcon(R.drawable.ic_help_actionbar);
     80         return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
     81     }
     82 
     83     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
     84             String backupContext) {
     85         MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
     86         helpItem.setIcon(R.drawable.ic_help_actionbar);
     87         return prepareHelpMenuItem(activity, helpItem, activity.getString(helpUriResource),
     88                 backupContext);
     89     }
     90 
     91     /**
     92      * Prepares the help menu item by doing the following.
     93      * - If the helpUrlString is empty or null, the help menu item is made invisible.
     94      * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
     95      *   item to view the URL.
     96      *
     97      * @return returns whether the help menu item has been made visible.
     98      */
     99     public static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
    100             String helpUriString, String backupContext) {
    101         if (Global.getInt(activity.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
    102             return false;
    103         }
    104         if (TextUtils.isEmpty(helpUriString)) {
    105             // The help url string is empty or null, so set the help menu item to be invisible.
    106             helpMenuItem.setVisible(false);
    107 
    108             // return that the help menu item is not visible (i.e. false)
    109             return false;
    110         } else {
    111             final Intent intent = getHelpIntent(activity, helpUriString, backupContext);
    112 
    113             // Set the intent to the help menu item, show the help menu item in the overflow
    114             // menu, and make it visible.
    115             if (intent != null) {
    116                 helpMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
    117                     @Override
    118                     public boolean onMenuItemClick(MenuItem item) {
    119                         MetricsLogger.action(activity,
    120                             MetricsEvent.ACTION_SETTING_HELP_AND_FEEDBACK,
    121                             intent.getStringExtra(EXTRA_CONTEXT));
    122                         try {
    123                             activity.startActivityForResult(intent, 0);
    124                         } catch (ActivityNotFoundException exc) {
    125                             Log.e(TAG, "No activity found for intent: " + intent);
    126                         }
    127                         return true;
    128                     }
    129                 });
    130                 helpMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    131                 helpMenuItem.setVisible(true);
    132             } else {
    133                 helpMenuItem.setVisible(false);
    134                 return false;
    135             }
    136 
    137             // return that the help menu item is visible (i.e., true)
    138             return true;
    139         }
    140     }
    141 
    142     public static Intent getHelpIntent(Context context, String helpUriString,
    143             String backupContext) {
    144         if (Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
    145             return null;
    146         }
    147         // Try to handle as Intent Uri, otherwise just treat as Uri.
    148         try {
    149             Intent intent = Intent.parseUri(helpUriString,
    150                     Intent.URI_ANDROID_APP_SCHEME | Intent.URI_INTENT_SCHEME);
    151             addIntentParameters(context, intent, backupContext, true /* sendPackageName */);
    152             ComponentName component = intent.resolveActivity(context.getPackageManager());
    153             if (component != null) {
    154                 return intent;
    155             } else if (intent.hasExtra(EXTRA_BACKUP_URI)) {
    156                 // This extra contains a backup URI for when the intent isn't available.
    157                 return getHelpIntent(context, intent.getStringExtra(EXTRA_BACKUP_URI),
    158                         backupContext);
    159             } else {
    160                 return null;
    161             }
    162         } catch (URISyntaxException e) {
    163         }
    164         // The help url string exists, so first add in some extra query parameters.
    165         final Uri fullUri = uriWithAddedParameters(context, Uri.parse(helpUriString));
    166 
    167         // Then, create an intent that will be fired when the user
    168         // selects this help menu item.
    169         Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
    170         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    171                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    172         return intent;
    173     }
    174 
    175     public static void addIntentParameters(Context context, Intent intent, String backupContext,
    176             boolean sendPackageName) {
    177         if (!intent.hasExtra(EXTRA_CONTEXT)) {
    178             // Insert some context if none exists.
    179             intent.putExtra(EXTRA_CONTEXT, backupContext);
    180         }
    181 
    182         Resources resources = context.getResources();
    183         boolean includePackageName =
    184                 resources.getBoolean(com.android.internal.R.bool.config_sendPackageName);
    185 
    186         if (sendPackageName && includePackageName) {
    187             String[] packageNameKey =
    188                     {resources.getString(com.android.internal.R.string.config_helpPackageNameKey)};
    189             String[] packageNameValue =
    190                     {resources.getString(
    191                             com.android.internal.R.string.config_helpPackageNameValue)};
    192             String helpIntentExtraKey =
    193                     resources.getString(com.android.internal.R.string.config_helpIntentExtraKey);
    194             String helpIntentNameKey =
    195                     resources.getString(com.android.internal.R.string.config_helpIntentNameKey);
    196             String feedbackIntentExtraKey =
    197                     resources.getString(
    198                             com.android.internal.R.string.config_feedbackIntentExtraKey);
    199             String feedbackIntentNameKey =
    200                     resources.getString(com.android.internal.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