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 package android.view.textclassifier.intent;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.content.Intent;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.text.TextUtils;
24 import android.view.textclassifier.Log;
25 import android.view.textclassifier.TextClassifier;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import com.google.android.textclassifier.NamedVariant;
30 import com.google.android.textclassifier.RemoteActionTemplate;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * Creates intents based on {@link RemoteActionTemplate} objects.
37  *
38  * @hide
39  */
40 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
41 public final class TemplateIntentFactory {
42     private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
43 
44     /**
45      * Constructs and returns a list of {@link LabeledIntent} based on the given templates.
46      */
47     @Nullable
create( @onNull RemoteActionTemplate[] remoteActionTemplates)48     public List<LabeledIntent> create(
49             @NonNull RemoteActionTemplate[] remoteActionTemplates) {
50         if (remoteActionTemplates.length == 0) {
51             return new ArrayList<>();
52         }
53         final List<LabeledIntent> labeledIntents = new ArrayList<>();
54         for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) {
55             if (!isValidTemplate(remoteActionTemplate)) {
56                 Log.w(TAG, "Invalid RemoteActionTemplate skipped.");
57                 continue;
58             }
59             labeledIntents.add(
60                     new LabeledIntent(
61                             remoteActionTemplate.titleWithoutEntity,
62                             remoteActionTemplate.titleWithEntity,
63                             remoteActionTemplate.description,
64                             remoteActionTemplate.descriptionWithAppName,
65                             createIntent(remoteActionTemplate),
66                             remoteActionTemplate.requestCode == null
67                                     ? LabeledIntent.DEFAULT_REQUEST_CODE
68                                     : remoteActionTemplate.requestCode));
69         }
70         return labeledIntents;
71     }
72 
isValidTemplate(@ullable RemoteActionTemplate remoteActionTemplate)73     private static boolean isValidTemplate(@Nullable RemoteActionTemplate remoteActionTemplate) {
74         if (remoteActionTemplate == null) {
75             Log.w(TAG, "Invalid RemoteActionTemplate: is null");
76             return false;
77         }
78         if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
79                 && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
80             Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
81             return false;
82         }
83         if (TextUtils.isEmpty(remoteActionTemplate.description)) {
84             Log.w(TAG, "Invalid RemoteActionTemplate: description is null");
85             return false;
86         }
87         if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) {
88             Log.w(TAG, "Invalid RemoteActionTemplate: package name is set");
89             return false;
90         }
91         if (TextUtils.isEmpty(remoteActionTemplate.action)) {
92             Log.w(TAG, "Invalid RemoteActionTemplate: intent action not set");
93             return false;
94         }
95         return true;
96     }
97 
createIntent(RemoteActionTemplate remoteActionTemplate)98     private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) {
99         final Intent intent = new Intent(remoteActionTemplate.action);
100         final Uri uri = TextUtils.isEmpty(remoteActionTemplate.data)
101                 ? null : Uri.parse(remoteActionTemplate.data).normalizeScheme();
102         final String type = TextUtils.isEmpty(remoteActionTemplate.type)
103                 ? null : Intent.normalizeMimeType(remoteActionTemplate.type);
104         intent.setDataAndType(uri, type);
105         intent.setFlags(remoteActionTemplate.flags == null ? 0 : remoteActionTemplate.flags);
106         if (remoteActionTemplate.category != null) {
107             for (String category : remoteActionTemplate.category) {
108                 if (category != null) {
109                     intent.addCategory(category);
110                 }
111             }
112         }
113         intent.putExtras(nameVariantsToBundle(remoteActionTemplate.extras));
114         return intent;
115     }
116 
117     /**
118      * Converts an array of {@link NamedVariant} to a Bundle and returns it.
119      */
nameVariantsToBundle(@ullable NamedVariant[] namedVariants)120     public static Bundle nameVariantsToBundle(@Nullable NamedVariant[] namedVariants) {
121         if (namedVariants == null) {
122             return Bundle.EMPTY;
123         }
124         Bundle bundle = new Bundle();
125         for (NamedVariant namedVariant : namedVariants) {
126             if (namedVariant == null) {
127                 continue;
128             }
129             switch (namedVariant.getType()) {
130                 case NamedVariant.TYPE_INT:
131                     bundle.putInt(namedVariant.getName(), namedVariant.getInt());
132                     break;
133                 case NamedVariant.TYPE_LONG:
134                     bundle.putLong(namedVariant.getName(), namedVariant.getLong());
135                     break;
136                 case NamedVariant.TYPE_FLOAT:
137                     bundle.putFloat(namedVariant.getName(), namedVariant.getFloat());
138                     break;
139                 case NamedVariant.TYPE_DOUBLE:
140                     bundle.putDouble(namedVariant.getName(), namedVariant.getDouble());
141                     break;
142                 case NamedVariant.TYPE_BOOL:
143                     bundle.putBoolean(namedVariant.getName(), namedVariant.getBool());
144                     break;
145                 case NamedVariant.TYPE_STRING:
146                     bundle.putString(namedVariant.getName(), namedVariant.getString());
147                     break;
148                 default:
149                     Log.w(TAG,
150                             "Unsupported type found in nameVariantsToBundle : "
151                                     + namedVariant.getType());
152             }
153         }
154         return bundle;
155     }
156 }
157