1 /*
2  * Copyright (C) 2021 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 package com.android.launcher3.model.data;
17 
18 import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
19 
20 import android.app.PendingIntent;
21 import android.content.Intent;
22 import android.graphics.drawable.Icon;
23 import android.os.Process;
24 import android.os.UserHandle;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.launcher3.LauncherAppState;
29 import com.android.launcher3.LauncherModel;
30 import com.android.launcher3.LauncherSettings;
31 import com.android.launcher3.icons.LauncherIcons;
32 import com.android.launcher3.logger.LauncherAtom.ItemInfo;
33 import com.android.launcher3.logger.LauncherAtom.SearchActionItem;
34 import com.android.launcher3.model.AllAppsList;
35 import com.android.launcher3.model.BaseModelUpdateTask;
36 import com.android.launcher3.model.BgDataModel;
37 
38 /**
39  * Represents a SearchAction with in launcher
40  */
41 public class SearchActionItemInfo extends ItemInfoWithIcon {
42 
43     public static final int FLAG_SHOULD_START = 1 << 1;
44     public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
45     public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
46     public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
47     public static final int FLAG_BADGE_WITH_COMPONENT_NAME = 1 << 5;
48     public static final int FLAG_ALLOW_PINNING = 1 << 6;
49     public static final int FLAG_SEARCH_IN_APP = 1 << 7;
50 
51     private String mFallbackPackageName;
52     private int mFlags = 0;
53     private Icon mIcon;
54 
55     // If true title does not contain any personal info and eligible for logging.
56     private boolean mIsPersonalTitle;
57     private Intent mIntent;
58 
59     private PendingIntent mPendingIntent;
60 
SearchActionItemInfo(Icon icon, String packageName, UserHandle user, CharSequence title, boolean isPersonalTitle)61     public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
62             CharSequence title, boolean isPersonalTitle) {
63         mIsPersonalTitle = isPersonalTitle;
64         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
65         this.user = user == null ? Process.myUserHandle() : user;
66         this.title = title;
67         this.container = EXTENDED_CONTAINERS;
68         mFallbackPackageName = packageName;
69         mIcon = icon;
70     }
71 
SearchActionItemInfo(SearchActionItemInfo info)72     private SearchActionItemInfo(SearchActionItemInfo info) {
73         super(info);
74     }
75 
76     @Override
copyFrom(com.android.launcher3.model.data.ItemInfo info)77     public void copyFrom(com.android.launcher3.model.data.ItemInfo info) {
78         super.copyFrom(info);
79         SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
80         this.mFallbackPackageName = itemInfo.mFallbackPackageName;
81         this.mIcon = itemInfo.mIcon;
82         this.mFlags = itemInfo.mFlags;
83         this.mIsPersonalTitle = itemInfo.mIsPersonalTitle;
84     }
85 
86     /**
87      * Returns if multiple flags are all available.
88      */
hasFlags(int flags)89     public boolean hasFlags(int flags) {
90         return (mFlags & flags) != 0;
91     }
92 
setFlags(int flags)93     public void setFlags(int flags) {
94         mFlags |= flags;
95     }
96 
97     @Override
getIntent()98     public Intent getIntent() {
99         return mIntent;
100     }
101 
102     /**
103      * Setter for mIntent with assertion for null value mPendingIntent
104      */
setIntent(Intent intent)105     public void setIntent(Intent intent) {
106         if (mPendingIntent != null && intent != null) {
107             throw new RuntimeException(
108                     "SearchActionItemInfo can only have either an Intent or a PendingIntent");
109         }
110         mIntent = intent;
111     }
112 
getPendingIntent()113     public PendingIntent getPendingIntent() {
114         return mPendingIntent;
115     }
116 
117     /**
118      * Setter of mPendingIntent with assertion for null value mIntent
119      */
setPendingIntent(PendingIntent pendingIntent)120     public void setPendingIntent(PendingIntent pendingIntent) {
121         if (mIntent != null && pendingIntent != null) {
122             throw new RuntimeException(
123                     "SearchActionItemInfo can only have either an Intent or a PendingIntent");
124         }
125         mPendingIntent = pendingIntent;
126     }
127 
128     @Nullable
getIcon()129     public Icon getIcon() {
130         return mIcon;
131     }
132 
133     @Override
clone()134     public ItemInfoWithIcon clone() {
135         return new SearchActionItemInfo(this);
136     }
137 
138     @Override
buildProto(FolderInfo fInfo)139     public ItemInfo buildProto(FolderInfo fInfo) {
140         SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
141                 .setPackageName(mFallbackPackageName);
142 
143         if (!mIsPersonalTitle) {
144             itemBuilder.setTitle(title.toString());
145         }
146         return getDefaultItemInfoBuilder()
147                 .setSearchActionItem(itemBuilder)
148                 .setContainerInfo(getContainerInfo())
149                 .build();
150     }
151 
152     /**
153      * Returns true if result supports drag/drop to home screen
154      */
supportsPinning()155     public boolean supportsPinning() {
156         return hasFlags(FLAG_ALLOW_PINNING) && getIntentPackageName() != null;
157     }
158 
159     /**
160      * Creates a {@link WorkspaceItemInfo} coorsponding to search action to be stored in launcher db
161      */
createWorkspaceItem(LauncherModel model)162     public WorkspaceItemInfo createWorkspaceItem(LauncherModel model) {
163         WorkspaceItemInfo info = new WorkspaceItemInfo();
164         info.title = title;
165         info.bitmap = bitmap;
166         info.intent = mIntent;
167 
168         if (hasFlags(FLAG_SHOULD_START_FOR_RESULT)) {
169             info.options |= WorkspaceItemInfo.FLAG_START_FOR_RESULT;
170         }
171 
172         model.enqueueModelUpdateTask(new BaseModelUpdateTask() {
173             @Override
174             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
175 
176                 model.updateAndBindWorkspaceItem(() -> {
177                     PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user);
178                     app.getIconCache().getTitleAndIconForApp(pkgInfo, false);
179                     try (LauncherIcons li = LauncherIcons.obtain(app.getContext())) {
180                         info.bitmap = li.badgeBitmap(info.bitmap.icon, pkgInfo.bitmap);
181                     }
182                     return info;
183                 });
184             }
185         });
186         return info;
187     }
188 
189     @Nullable
getIntentPackageName()190     private String getIntentPackageName() {
191         if (mIntent != null) {
192             if (mIntent.getPackage() != null) return mIntent.getPackage();
193             return mFallbackPackageName;
194         }
195         return null;
196     }
197 }
198