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