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.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Intent;
22 import android.os.Bundle;
23 import android.os.UserHandle;
24 import android.util.Log;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.List;
32 
33 /**
34  * Represents a "stack" of chooser targets for various activities within the same component.
35  */
36 public class MultiDisplayResolveInfo extends DisplayResolveInfo {
37 
38     final ArrayList<DisplayResolveInfo> mTargetInfos;
39 
40     // Index of selected target
41     private int mSelected = -1;
42 
43     /**
44      * @param targetInfos A list of targets in this stack. The first item is treated as the
45      * "representative" that provides the main icon, title, etc.
46      */
newMultiDisplayResolveInfo( List<DisplayResolveInfo> targetInfos)47     public static MultiDisplayResolveInfo newMultiDisplayResolveInfo(
48             List<DisplayResolveInfo> targetInfos) {
49         return new MultiDisplayResolveInfo(targetInfos);
50     }
51 
52     /**
53      * @param targetInfos A list of targets in this stack. The first item is treated as the
54      * "representative" that provides the main icon, title, etc.
55      */
MultiDisplayResolveInfo(List<DisplayResolveInfo> targetInfos)56     private MultiDisplayResolveInfo(List<DisplayResolveInfo> targetInfos) {
57         super(targetInfos.get(0));
58         mTargetInfos = new ArrayList<>(targetInfos);
59     }
60 
61     @Override
isMultiDisplayResolveInfo()62     public final boolean isMultiDisplayResolveInfo() {
63         return true;
64     }
65 
66     @Override
getExtendedInfo()67     public CharSequence getExtendedInfo() {
68         // Never show subtitle for stacked apps
69         return null;
70     }
71 
72     /**
73      * List of all {@link DisplayResolveInfo}s included in this target.
74      * TODO: provide as a generic {@code List<DisplayResolveInfo>} once
75      *  {@link com.android.intentresolver.ChooserActivity} stops requiring the signature to match
76      *  that of the other "lists" it builds up.
77      */
78     @Override
getAllDisplayTargets()79     public ArrayList<DisplayResolveInfo> getAllDisplayTargets() {
80         return mTargetInfos;
81     }
82 
setSelected(int selected)83     public void setSelected(int selected) {
84         mSelected = selected;
85     }
86 
87     /**
88      * Return selected target.
89      */
getSelectedTarget()90     public DisplayResolveInfo getSelectedTarget() {
91         return hasSelected() ? mTargetInfos.get(mSelected) : null;
92     }
93 
94     /**
95      * Whether or not the user has selected a specific target for this MultiInfo.
96      */
hasSelected()97     public boolean hasSelected() {
98         return mSelected >= 0;
99     }
100 
101     @Override
102     @Nullable
tryToCloneWithAppliedRefinement(Intent proposedRefinement)103     public MultiDisplayResolveInfo tryToCloneWithAppliedRefinement(Intent proposedRefinement) {
104         final int size = mTargetInfos.size();
105         ArrayList<DisplayResolveInfo> targetInfos = new ArrayList<>(size);
106         for (int i = 0; i < size; i++) {
107             DisplayResolveInfo target = mTargetInfos.get(i);
108             DisplayResolveInfo targetClone = (i == mSelected)
109                     ? target.tryToCloneWithAppliedRefinement(proposedRefinement)
110                     : new DisplayResolveInfo(target);
111             if (targetClone == null) {
112                 return null;
113             }
114             targetInfos.add(targetClone);
115         }
116         MultiDisplayResolveInfo clone = new MultiDisplayResolveInfo(targetInfos);
117         clone.mSelected = mSelected;
118         return clone;
119     }
120 
121     @Override
startAsCaller(Activity activity, Bundle options, int userId)122     public boolean startAsCaller(Activity activity, Bundle options, int userId) {
123         return mTargetInfos.get(mSelected).startAsCaller(activity, options, userId);
124     }
125 
126     @Override
127     @NonNull
getResolvedComponentName()128     public ComponentName getResolvedComponentName() {
129         if (hasSelected()) {
130             return mTargetInfos.get(mSelected).getResolvedComponentName();
131         }
132         // It is not expected to have this method be called on an unselected multi-display item.
133         // Call super to preserve the legacy (most likely erroneous) behavior.
134         Log.wtf(
135                 "ChooserActivity",
136                 "retrieving ResolvedComponentName from an unselected MultiDisplayResolveInfo");
137         return super.getResolvedComponentName();
138     }
139 
140     @Override
startAsUser(Activity activity, Bundle options, UserHandle user)141     public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
142         return mTargetInfos.get(mSelected).startAsUser(activity, options, user);
143     }
144 
145     @Override
getTargetIntent()146     public Intent getTargetIntent() {
147         return mTargetInfos.get(mSelected).getTargetIntent();
148     }
149 
150     @Override
getAllSourceIntents()151     public List<Intent> getAllSourceIntents() {
152         return hasSelected()
153                 ? mTargetInfos.get(mSelected).getAllSourceIntents()
154                 : Collections.emptyList();
155     }
156 }
157