• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.intentresolver.chooser;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.content.ComponentName;
23 import android.content.Intent;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.ResolveInfo;
27 import android.os.Bundle;
28 import android.os.UserHandle;
29 
30 import com.android.intentresolver.TargetPresentationGetter;
31 
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 
36 /**
37  * A TargetInfo plus additional information needed to render it (such as icon and label) and
38  * resolve it to an activity.
39  */
40 public class DisplayResolveInfo implements TargetInfo {
41     private final ResolveInfo mResolveInfo;
42     private CharSequence mDisplayLabel;
43     private CharSequence mExtendedInfo;
44     private final Intent mResolvedIntent;
45     private final List<Intent> mSourceIntents = new ArrayList<>();
46     private final boolean mIsSuspended;
47     private TargetPresentationGetter mPresentationGetter;
48     private boolean mPinned = false;
49     private final IconHolder mDisplayIconHolder = new SettableIconHolder();
50 
51     /** Create a new {@code DisplayResolveInfo} instance. */
newDisplayResolveInfo( Intent originalIntent, ResolveInfo resolveInfo, @NonNull Intent resolvedIntent, @Nullable TargetPresentationGetter presentationGetter)52     public static DisplayResolveInfo newDisplayResolveInfo(
53             Intent originalIntent,
54             ResolveInfo resolveInfo,
55             @NonNull Intent resolvedIntent,
56             @Nullable TargetPresentationGetter presentationGetter) {
57         return newDisplayResolveInfo(
58                 originalIntent,
59                 resolveInfo,
60                 /* displayLabel=*/ null,
61                 /* extendedInfo=*/ null,
62                 resolvedIntent,
63                 presentationGetter);
64     }
65 
66     /** Create a new {@code DisplayResolveInfo} instance. */
newDisplayResolveInfo( Intent originalIntent, ResolveInfo resolveInfo, CharSequence displayLabel, CharSequence extendedInfo, @NonNull Intent resolvedIntent, @Nullable TargetPresentationGetter presentationGetter)67     public static DisplayResolveInfo newDisplayResolveInfo(
68             Intent originalIntent,
69             ResolveInfo resolveInfo,
70             CharSequence displayLabel,
71             CharSequence extendedInfo,
72             @NonNull Intent resolvedIntent,
73             @Nullable TargetPresentationGetter presentationGetter) {
74         return new DisplayResolveInfo(
75                 originalIntent,
76                 resolveInfo,
77                 displayLabel,
78                 extendedInfo,
79                 resolvedIntent,
80                 presentationGetter);
81     }
82 
DisplayResolveInfo( Intent originalIntent, ResolveInfo resolveInfo, CharSequence displayLabel, CharSequence extendedInfo, @NonNull Intent resolvedIntent, @Nullable TargetPresentationGetter presentationGetter)83     private DisplayResolveInfo(
84             Intent originalIntent,
85             ResolveInfo resolveInfo,
86             CharSequence displayLabel,
87             CharSequence extendedInfo,
88             @NonNull Intent resolvedIntent,
89             @Nullable TargetPresentationGetter presentationGetter) {
90         mSourceIntents.add(originalIntent);
91         mResolveInfo = resolveInfo;
92         mDisplayLabel = displayLabel;
93         mExtendedInfo = extendedInfo;
94         mPresentationGetter = presentationGetter;
95 
96         final ActivityInfo ai = mResolveInfo.activityInfo;
97         mIsSuspended = (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
98 
99         mResolvedIntent = createResolvedIntent(resolvedIntent, ai);
100     }
101 
DisplayResolveInfo( DisplayResolveInfo other, @Nullable Intent baseIntentToSend, TargetPresentationGetter presentationGetter)102     private DisplayResolveInfo(
103             DisplayResolveInfo other,
104             @Nullable Intent baseIntentToSend,
105             TargetPresentationGetter presentationGetter) {
106         mSourceIntents.addAll(other.getAllSourceIntents());
107         mResolveInfo = other.mResolveInfo;
108         mIsSuspended = other.mIsSuspended;
109         mDisplayLabel = other.mDisplayLabel;
110         mExtendedInfo = other.mExtendedInfo;
111 
112         mResolvedIntent = createResolvedIntent(
113                 baseIntentToSend == null ? other.mResolvedIntent : baseIntentToSend,
114                 mResolveInfo.activityInfo);
115         mPresentationGetter = presentationGetter;
116 
117         mDisplayIconHolder.setDisplayIcon(other.mDisplayIconHolder.getDisplayIcon());
118     }
119 
DisplayResolveInfo(DisplayResolveInfo other)120     protected DisplayResolveInfo(DisplayResolveInfo other) {
121         mSourceIntents.addAll(other.getAllSourceIntents());
122         mResolveInfo = other.mResolveInfo;
123         mIsSuspended = other.mIsSuspended;
124         mDisplayLabel = other.mDisplayLabel;
125         mExtendedInfo = other.mExtendedInfo;
126         mResolvedIntent = other.mResolvedIntent;
127         mPresentationGetter = other.mPresentationGetter;
128 
129         mDisplayIconHolder.setDisplayIcon(other.mDisplayIconHolder.getDisplayIcon());
130     }
131 
createResolvedIntent(Intent resolvedIntent, ActivityInfo ai)132     private static Intent createResolvedIntent(Intent resolvedIntent, ActivityInfo ai) {
133         final Intent result = new Intent(resolvedIntent);
134         result.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
135                 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
136         result.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name));
137         return result;
138     }
139 
140     @Override
isDisplayResolveInfo()141     public final boolean isDisplayResolveInfo() {
142         return true;
143     }
144 
getResolveInfo()145     public ResolveInfo getResolveInfo() {
146         return mResolveInfo;
147     }
148 
getDisplayLabel()149     public CharSequence getDisplayLabel() {
150         if (mDisplayLabel == null && mPresentationGetter != null) {
151             mDisplayLabel = mPresentationGetter.getLabel();
152             mExtendedInfo = mPresentationGetter.getSubLabel();
153         }
154         return mDisplayLabel;
155     }
156 
hasDisplayLabel()157     public boolean hasDisplayLabel() {
158         return mDisplayLabel != null;
159     }
160 
setDisplayLabel(CharSequence displayLabel)161     public void setDisplayLabel(CharSequence displayLabel) {
162         mDisplayLabel = displayLabel;
163     }
164 
setExtendedInfo(CharSequence extendedInfo)165     public void setExtendedInfo(CharSequence extendedInfo) {
166         mExtendedInfo = extendedInfo;
167     }
168 
169     @Override
getDisplayIconHolder()170     public IconHolder getDisplayIconHolder() {
171         return mDisplayIconHolder;
172     }
173 
174     @Override
175     @Nullable
tryToCloneWithAppliedRefinement(Intent proposedRefinement)176     public DisplayResolveInfo tryToCloneWithAppliedRefinement(Intent proposedRefinement) {
177         Intent matchingBase =
178                 getAllSourceIntents()
179                         .stream()
180                         .filter(i -> i.filterEquals(proposedRefinement))
181                         .findFirst()
182                         .orElse(null);
183         if (matchingBase == null) {
184             return null;
185         }
186 
187         Intent merged = new Intent(matchingBase);
188         merged.fillIn(proposedRefinement, 0);
189         return new DisplayResolveInfo(this, merged, mPresentationGetter);
190     }
191 
192     @Override
getAllSourceIntents()193     public List<Intent> getAllSourceIntents() {
194         return mSourceIntents;
195     }
196 
197     @Override
getAllDisplayTargets()198     public ArrayList<DisplayResolveInfo> getAllDisplayTargets() {
199         return new ArrayList<>(Arrays.asList(this));
200     }
201 
addAlternateSourceIntent(Intent alt)202     public void addAlternateSourceIntent(Intent alt) {
203         mSourceIntents.add(alt);
204     }
205 
getExtendedInfo()206     public CharSequence getExtendedInfo() {
207         return mExtendedInfo;
208     }
209 
getResolvedIntent()210     public Intent getResolvedIntent() {
211         return mResolvedIntent;
212     }
213 
214     @Override
getResolvedComponentName()215     public ComponentName getResolvedComponentName() {
216         return new ComponentName(mResolveInfo.activityInfo.packageName,
217                 mResolveInfo.activityInfo.name);
218     }
219 
220     @Override
startAsCaller(Activity activity, Bundle options, int userId)221     public boolean startAsCaller(Activity activity, Bundle options, int userId) {
222         TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
223         activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
224         return true;
225     }
226 
227     @Override
startAsUser(Activity activity, Bundle options, UserHandle user)228     public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
229         TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
230         // TODO: is this equivalent to `startActivityAsCaller` with `ignoreTargetSecurity=true`? If
231         // so, we can consolidate on the one API method to show that this flag is the only
232         // distinction between `startAsCaller` and `startAsUser`. We can even bake that flag into
233         // the `TargetActivityStarter` upfront since it just reflects our "safe forwarding mode" --
234         // which is constant for the duration of our lifecycle, leaving clients no other
235         // responsibilities in this logic.
236         activity.startActivityAsUser(mResolvedIntent, options, user);
237         return false;
238     }
239 
240     @Override
getTargetIntent()241     public Intent getTargetIntent() {
242         return mResolvedIntent;
243     }
244 
isSuspended()245     public boolean isSuspended() {
246         return mIsSuspended;
247     }
248 
249     @Override
isPinned()250     public boolean isPinned() {
251         return mPinned;
252     }
253 
setPinned(boolean pinned)254     public void setPinned(boolean pinned) {
255         mPinned = pinned;
256     }
257 }
258