• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*/
HelpUtils()74     private HelpUtils() { }
75 
prepareHelpMenuItem(Activity activity, Menu menu, String helpUri, String backupContext)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 
prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource, String backupContext)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      */
prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem, String helpUriString, String backupContext)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 
getHelpIntent(Context context, String helpUriString, String backupContext)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 
addIntentParameters(Context context, Intent intent, String backupContext, boolean sendPackageName)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      */
uriWithAddedParameters(Context context, Uri baseUri)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