1 /*
2  * Copyright (C) 2017 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.launcher3.pm;
18 
19 import android.annotation.TargetApi;
20 import android.app.Activity;
21 import android.content.ActivityNotFoundException;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentSender;
26 import android.content.pm.ActivityInfo;
27 import android.content.pm.LauncherActivityInfo;
28 import android.content.pm.LauncherApps;
29 import android.content.pm.PackageManager;
30 import android.graphics.drawable.Drawable;
31 import android.os.Build;
32 import android.os.Process;
33 import android.os.UserHandle;
34 import android.util.Log;
35 import android.widget.Toast;
36 
37 import androidx.annotation.Nullable;
38 
39 import com.android.launcher3.LauncherSettings;
40 import com.android.launcher3.R;
41 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
42 import com.android.launcher3.icons.IconCache;
43 import com.android.launcher3.model.data.WorkspaceItemInfo;
44 import com.android.launcher3.util.PackageUserKey;
45 
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.List;
49 
50 /**
51  * Wrapper class for representing a shortcut configure activity.
52  */
53 public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAndIcon {
54 
55     private static final String TAG = "SCActivityInfo";
56 
57     private final ComponentName mCn;
58     private final UserHandle mUser;
59 
ShortcutConfigActivityInfo(ComponentName cn, UserHandle user)60     protected ShortcutConfigActivityInfo(ComponentName cn, UserHandle user) {
61         mCn = cn;
62         mUser = user;
63     }
64 
65     @Override
getComponent()66     public ComponentName getComponent() {
67         return mCn;
68     }
69 
70     @Override
getUser()71     public UserHandle getUser() {
72         return mUser;
73     }
74 
getItemType()75     public int getItemType() {
76         return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
77     }
78 
79     @Override
getFullResIcon(IconCache cache)80     public abstract Drawable getFullResIcon(IconCache cache);
81 
82     /**
83      * Return a WorkspaceItemInfo, if it can be created directly on drop, without requiring any
84      * {@link #startConfigActivity(Activity, int)}.
85      */
createWorkspaceItemInfo()86     public WorkspaceItemInfo createWorkspaceItemInfo() {
87         return null;
88     }
89 
startConfigActivity(Activity activity, int requestCode)90     public boolean startConfigActivity(Activity activity, int requestCode) {
91         Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
92                 .setComponent(getComponent());
93         try {
94             activity.startActivityForResult(intent, requestCode);
95             return true;
96         } catch (ActivityNotFoundException e) {
97             Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
98         } catch (SecurityException e) {
99             Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
100             Log.e(TAG, "Launcher does not have the permission to launch " + intent
101                     + ". Make sure to create a MAIN intent-filter for the corresponding activity "
102                     + "or use the exported attribute for this activity.", e);
103         }
104         return false;
105     }
106 
107     /**
108      * Returns true if various properties ({@link #getLabel(PackageManager)},
109      * {@link #getFullResIcon}) can be safely persisted.
110      */
isPersistable()111     public boolean isPersistable() {
112         return true;
113     }
114 
115     static class ShortcutConfigActivityInfoVL extends ShortcutConfigActivityInfo {
116 
117         private final ActivityInfo mInfo;
118 
ShortcutConfigActivityInfoVL(ActivityInfo info)119         ShortcutConfigActivityInfoVL(ActivityInfo info) {
120             super(new ComponentName(info.packageName, info.name), Process.myUserHandle());
121             mInfo = info;
122         }
123 
124         @Override
getLabel(PackageManager pm)125         public CharSequence getLabel(PackageManager pm) {
126             return mInfo.loadLabel(pm);
127         }
128 
129         @Override
getFullResIcon(IconCache cache)130         public Drawable getFullResIcon(IconCache cache) {
131             return cache.getFullResIcon(mInfo);
132         }
133     }
134 
135     @TargetApi(26)
136     public static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
137 
138         private final LauncherActivityInfo mInfo;
139 
ShortcutConfigActivityInfoVO(LauncherActivityInfo info)140         public ShortcutConfigActivityInfoVO(LauncherActivityInfo info) {
141             super(info.getComponentName(), info.getUser());
142             mInfo = info;
143         }
144 
145         @Override
getLabel(PackageManager pm)146         public CharSequence getLabel(PackageManager pm) {
147             return mInfo.getLabel();
148         }
149 
150         @Override
getFullResIcon(IconCache cache)151         public Drawable getFullResIcon(IconCache cache) {
152             return cache.getFullResIcon(mInfo);
153         }
154 
155         @Override
startConfigActivity(Activity activity, int requestCode)156         public boolean startConfigActivity(Activity activity, int requestCode) {
157             if (getUser().equals(Process.myUserHandle())) {
158                 return super.startConfigActivity(activity, requestCode);
159             }
160             IntentSender is = activity.getSystemService(LauncherApps.class)
161                     .getShortcutConfigActivityIntent(mInfo);
162             try {
163                 activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0);
164                 return true;
165             } catch (IntentSender.SendIntentException e) {
166                 Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
167                 return false;
168             }
169         }
170     }
171 
queryList( Context context, @Nullable PackageUserKey packageUser)172     public static List<ShortcutConfigActivityInfo> queryList(
173             Context context, @Nullable PackageUserKey packageUser) {
174         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
175         UserHandle myUser = Process.myUserHandle();
176 
177         final List<UserHandle> users;
178         final String packageName;
179         if (packageUser == null) {
180             users = UserCache.INSTANCE.get(context).getUserProfiles();
181             packageName = null;
182         } else {
183             users = Collections.singletonList(packageUser.mUser);
184             packageName = packageUser.mPackageName;
185         }
186         LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
187         for (UserHandle user : users) {
188             boolean ignoreTargetSdk = myUser.equals(user);
189             for (LauncherActivityInfo activityInfo :
190                     launcherApps.getShortcutConfigActivityList(packageName, user)) {
191                 if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
192                         >= Build.VERSION_CODES.O) {
193                     result.add(new ShortcutConfigActivityInfoVO(activityInfo));
194                 }
195             }
196         }
197         return result;
198     }
199 }
200