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 = Integer.toString(info.versionCode); 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