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