• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.settings.shortcut;
18 
19 import static com.android.settings.shortcut.Shortcuts.SHORTCUT_PROBE;
20 
21 import android.app.Activity;
22 import android.app.settings.SettingsEnums;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.content.pm.ShortcutInfo;
29 import android.content.pm.ShortcutManager;
30 import android.net.ConnectivityManager;
31 import android.util.Log;
32 
33 import androidx.annotation.VisibleForTesting;
34 import androidx.preference.Preference;
35 import androidx.preference.PreferenceCategory;
36 import androidx.preference.PreferenceGroup;
37 
38 import com.android.settings.R;
39 import com.android.settings.Settings;
40 import com.android.settings.Settings.DataUsageSummaryActivity;
41 import com.android.settings.Settings.TetherSettingsActivity;
42 import com.android.settings.Settings.WifiTetherSettingsActivity;
43 import com.android.settings.core.BasePreferenceController;
44 import com.android.settings.gestures.OneHandedSettingsUtils;
45 import com.android.settings.network.SubscriptionUtil;
46 import com.android.settings.network.telephony.MobileNetworkUtils;
47 import com.android.settings.overlay.FeatureFactory;
48 import com.android.settings.wifi.WifiUtils;
49 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
50 
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.List;
55 
56 /**
57  * {@link BasePreferenceController} that populates a list of widgets that Settings app support.
58  */
59 public class CreateShortcutPreferenceController extends BasePreferenceController {
60 
61     private static final String TAG = "CreateShortcutPrefCtrl";
62 
63     private final ShortcutManager mShortcutManager;
64     private final PackageManager mPackageManager;
65     private final ConnectivityManager mConnectivityManager;
66     private final MetricsFeatureProvider mMetricsFeatureProvider;
67     private Activity mHost;
68 
CreateShortcutPreferenceController(Context context, String preferenceKey)69     public CreateShortcutPreferenceController(Context context, String preferenceKey) {
70         super(context, preferenceKey);
71         mConnectivityManager =
72                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
73         mShortcutManager = context.getSystemService(ShortcutManager.class);
74         mPackageManager = context.getPackageManager();
75         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory()
76                 .getMetricsFeatureProvider();
77     }
78 
setActivity(Activity host)79     public void setActivity(Activity host) {
80         mHost = host;
81     }
82 
83     @Override
getAvailabilityStatus()84     public int getAvailabilityStatus() {
85         return AVAILABLE_UNSEARCHABLE;
86     }
87 
88     @Override
updateState(Preference preference)89     public void updateState(Preference preference) {
90         if (!(preference instanceof PreferenceGroup)) {
91             return;
92         }
93         final PreferenceGroup group = (PreferenceGroup) preference;
94         group.removeAll();
95         final List<ResolveInfo> shortcuts = queryShortcuts();
96         final Context uiContext = preference.getContext();
97         if (shortcuts.isEmpty()) {
98             return;
99         }
100         PreferenceCategory category = new PreferenceCategory(uiContext);
101         group.addPreference(category);
102         int bucket = 0;
103         for (ResolveInfo info : shortcuts) {
104             // Priority is not consecutive (aka, jumped), add a divider between prefs.
105             final int currentBucket = info.priority / 10;
106             boolean needDivider = currentBucket != bucket;
107             bucket = currentBucket;
108             if (needDivider) {
109                 // add a new Category
110                 category = new PreferenceCategory(uiContext);
111                 group.addPreference(category);
112             }
113 
114             final Preference pref = new Preference(uiContext);
115             pref.setTitle(info.loadLabel(mPackageManager));
116             pref.setKey(info.activityInfo.getComponentName().flattenToString());
117             pref.setOnPreferenceClickListener(clickTarget -> {
118                 if (mHost == null) {
119                     return false;
120                 }
121                 final Intent shortcutIntent = createResultIntent(info);
122                 mHost.setResult(Activity.RESULT_OK, shortcutIntent);
123                 logCreateShortcut(info);
124                 mHost.finish();
125                 return true;
126             });
127             category.addPreference(pref);
128         }
129     }
130 
131     /**
132      * Create {@link Intent} that will be consumed by ShortcutManager, which later generates a
133      * launcher widget using this intent.
134      */
135     @VisibleForTesting
createResultIntent(ResolveInfo resolveInfo)136     Intent createResultIntent(ResolveInfo resolveInfo) {
137         ShortcutInfo info = Shortcuts.createShortcutInfo(mContext, resolveInfo);
138         Intent intent = mShortcutManager.createShortcutResultIntent(info);
139         if (intent == null) {
140             intent = new Intent();
141         }
142         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
143                 Intent.ShortcutIconResource.fromContext(mContext, R.mipmap.ic_launcher_settings))
144                 .putExtra(Intent.EXTRA_SHORTCUT_INTENT, info.getIntent())
145                 .putExtra(Intent.EXTRA_SHORTCUT_NAME, info.getShortLabel());
146 
147         final ActivityInfo activityInfo = resolveInfo.activityInfo;
148         if (activityInfo.icon != 0) {
149             intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, Shortcuts.createIcon(
150                     mContext,
151                     activityInfo.applicationInfo,
152                     activityInfo.icon,
153                     R.layout.shortcut_badge,
154                     mContext.getResources().getDimensionPixelSize(R.dimen.shortcut_size)));
155         }
156         return intent;
157     }
158 
159     /**
160      * Finds all shortcut supported by Settings.
161      */
162     @VisibleForTesting
queryShortcuts()163     List<ResolveInfo> queryShortcuts() {
164         final List<ResolveInfo> shortcuts = new ArrayList<>();
165         final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(SHORTCUT_PROBE,
166                 PackageManager.GET_META_DATA);
167 
168         if (activities == null) {
169             return null;
170         }
171         for (ResolveInfo info : activities) {
172             if (info.activityInfo.name.contains(
173                     Settings.OneHandedSettingsActivity.class.getSimpleName())) {
174                 if (!OneHandedSettingsUtils.isSupportOneHandedMode()) {
175                     continue;
176                 }
177             }
178             if (info.activityInfo.name.endsWith(TetherSettingsActivity.class.getSimpleName())) {
179                 if (!mConnectivityManager.isTetheringSupported()) {
180                     continue;
181                 }
182             }
183             if (info.activityInfo.name.endsWith(WifiTetherSettingsActivity.class.getSimpleName())) {
184                 if (!canShowWifiHotspot()) {
185                     Log.d(TAG, "Skipping Wi-Fi hotspot settings:" + info.activityInfo);
186                     continue;
187                 }
188             }
189             if (!info.activityInfo.applicationInfo.isSystemApp()) {
190                 Log.d(TAG, "Skipping non-system app: " + info.activityInfo);
191                 continue;
192             }
193             if (info.activityInfo.name.endsWith(DataUsageSummaryActivity.class.getSimpleName())) {
194                 if (!canShowDataUsage()) {
195                     Log.d(TAG, "Skipping data usage settings:" + info.activityInfo);
196                     continue;
197                 }
198             }
199             shortcuts.add(info);
200         }
201         Collections.sort(shortcuts, SHORTCUT_COMPARATOR);
202         return shortcuts;
203     }
204 
205     @VisibleForTesting
canShowDataUsage()206     boolean canShowDataUsage() {
207         return SubscriptionUtil.isSimHardwareVisible(mContext)
208                 && !MobileNetworkUtils.isMobileNetworkUserRestricted(mContext);
209     }
210 
211     @VisibleForTesting
canShowWifiHotspot()212     boolean canShowWifiHotspot() {
213         return WifiUtils.canShowWifiHotspot(mContext);
214     }
215 
logCreateShortcut(ResolveInfo info)216     private void logCreateShortcut(ResolveInfo info) {
217         if (info == null || info.activityInfo == null) {
218             return;
219         }
220         mMetricsFeatureProvider.action(
221                 mContext, SettingsEnums.ACTION_SETTINGS_CREATE_SHORTCUT,
222                 info.activityInfo.name);
223     }
224 
225     private static final Comparator<ResolveInfo> SHORTCUT_COMPARATOR =
226             (i1, i2) -> i1.priority - i2.priority;
227 }
228