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