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