• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
18 
19 import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_OTHERS;
20 import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_SELF;
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.app.PendingIntent;
24 import android.app.Person;
25 import android.app.RemoteAction;
26 import android.content.ComponentName;
27 import android.content.Intent;
28 import android.graphics.drawable.Icon;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.view.textclassifier.ConversationAction;
32 import android.view.textclassifier.ConversationActions;
33 import androidx.test.core.app.ApplicationProvider;
34 import androidx.test.ext.junit.runners.AndroidJUnit4;
35 import androidx.test.filters.SmallTest;
36 import com.android.textclassifier.common.intent.LabeledIntent;
37 import com.android.textclassifier.common.intent.TemplateIntentFactory;
38 import com.google.android.textclassifier.ActionsSuggestionsModel;
39 import com.google.android.textclassifier.RemoteActionTemplate;
40 import com.google.common.collect.ImmutableList;
41 import java.time.Instant;
42 import java.time.ZoneId;
43 import java.time.ZonedDateTime;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.Locale;
48 import java.util.function.Function;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 @SmallTest
53 @RunWith(AndroidJUnit4.class)
54 public class ActionsSuggestionsHelperTest {
55   private static final String LOCALE_TAG = Locale.US.toLanguageTag();
56   private static final Function<CharSequence, List<String>> LANGUAGE_DETECTOR =
57       charSequence -> Collections.singletonList(LOCALE_TAG);
58 
59   @Test
testToNativeMessages_emptyInput()60   public void testToNativeMessages_emptyInput() {
61     ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
62         ActionsSuggestionsHelper.toNativeMessages(ImmutableList.of(), LANGUAGE_DETECTOR);
63 
64     assertThat(conversationMessages).isEmpty();
65   }
66 
67   @Test
testToNativeMessages_noTextMessages()68   public void testToNativeMessages_noTextMessages() {
69     ConversationActions.Message messageWithoutText =
70         new ConversationActions.Message.Builder(PERSON_USER_OTHERS).build();
71 
72     ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
73         ActionsSuggestionsHelper.toNativeMessages(
74             ImmutableList.of(messageWithoutText), LANGUAGE_DETECTOR);
75 
76     assertThat(conversationMessages).isEmpty();
77   }
78 
79   @Test
testToNativeMessages_userIdEncoding()80   public void testToNativeMessages_userIdEncoding() {
81     Person.Builder userA = new Person.Builder().setName("userA").setKey("A");
82     Person.Builder userB = new Person.Builder().setName("userB").setKey("B");
83 
84     ConversationActions.Message firstMessage =
85         new ConversationActions.Message.Builder(userB.build()).setText("first").build();
86     ConversationActions.Message secondMessage =
87         new ConversationActions.Message.Builder(userA.build()).setText("second").build();
88     ConversationActions.Message thirdMessage =
89         new ConversationActions.Message.Builder(PERSON_USER_SELF).setText("third").build();
90     ConversationActions.Message fourthMessage =
91         new ConversationActions.Message.Builder(userA.build()).setText("fourth").build();
92 
93     ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
94         ActionsSuggestionsHelper.toNativeMessages(
95             Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
96             LANGUAGE_DETECTOR);
97 
98     assertThat(conversationMessages).hasLength(4);
99     assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
100     assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
101     assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
102     assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
103   }
104 
105   @Test
testToNativeMessages_referenceTime()106   public void testToNativeMessages_referenceTime() {
107     ConversationActions.Message firstMessage =
108         new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
109             .setText("first")
110             .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
111             .build();
112     ConversationActions.Message secondMessage =
113         new ConversationActions.Message.Builder(PERSON_USER_OTHERS).setText("second").build();
114     ConversationActions.Message thirdMessage =
115         new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
116             .setText("third")
117             .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
118             .build();
119 
120     ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
121         ActionsSuggestionsHelper.toNativeMessages(
122             Arrays.asList(firstMessage, secondMessage, thirdMessage), LANGUAGE_DETECTOR);
123 
124     assertThat(conversationMessages).hasLength(3);
125     assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
126     assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
127     assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
128   }
129 
130   @Test
testDeduplicateActions()131   public void testDeduplicateActions() {
132     Bundle phoneExtras = new Bundle();
133     Intent phoneIntent = new Intent();
134     phoneIntent.setComponent(new ComponentName("phone", "intent"));
135     ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
136 
137     Bundle anotherPhoneExtras = new Bundle();
138     Intent anotherPhoneIntent = new Intent();
139     anotherPhoneIntent.setComponent(new ComponentName("phone", "another.intent"));
140     ExtrasUtils.putActionIntent(anotherPhoneExtras, anotherPhoneIntent);
141 
142     Bundle urlExtras = new Bundle();
143     Intent urlIntent = new Intent();
144     urlIntent.setComponent(new ComponentName("url", "intent"));
145     ExtrasUtils.putActionIntent(urlExtras, urlIntent);
146 
147     PendingIntent pendingIntent =
148         PendingIntent.getActivity(ApplicationProvider.getApplicationContext(), 0, phoneIntent, 0);
149     Icon icon = Icon.createWithData(new byte[0], 0, 0);
150     ConversationAction action =
151         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
152             .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
153             .setExtras(phoneExtras)
154             .build();
155     ConversationAction actionWithSameLabel =
156         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
157             .setAction(new RemoteAction(icon, "label", "2", pendingIntent))
158             .setExtras(phoneExtras)
159             .build();
160     ConversationAction actionWithSamePackageButDifferentClass =
161         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
162             .setAction(new RemoteAction(icon, "label", "3", pendingIntent))
163             .setExtras(anotherPhoneExtras)
164             .build();
165     ConversationAction actionWithDifferentLabel =
166         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
167             .setAction(new RemoteAction(icon, "another_label", "4", pendingIntent))
168             .setExtras(phoneExtras)
169             .build();
170     ConversationAction actionWithDifferentPackage =
171         new ConversationAction.Builder(ConversationAction.TYPE_OPEN_URL)
172             .setAction(new RemoteAction(icon, "label", "5", pendingIntent))
173             .setExtras(urlExtras)
174             .build();
175     ConversationAction actionWithoutRemoteAction =
176         new ConversationAction.Builder(ConversationAction.TYPE_CREATE_REMINDER).build();
177 
178     List<ConversationAction> conversationActions =
179         ActionsSuggestionsHelper.removeActionsWithDuplicates(
180             Arrays.asList(
181                 action,
182                 actionWithSameLabel,
183                 actionWithSamePackageButDifferentClass,
184                 actionWithDifferentLabel,
185                 actionWithDifferentPackage,
186                 actionWithoutRemoteAction));
187 
188     assertThat(conversationActions).hasSize(3);
189     assertThat(conversationActions.get(0).getAction().getContentDescription().toString())
190         .isEqualTo("4");
191     assertThat(conversationActions.get(1).getAction().getContentDescription().toString())
192         .isEqualTo("5");
193     assertThat(conversationActions.get(2).getAction()).isNull();
194   }
195 
196   @Test
testDeduplicateActions_nullComponent()197   public void testDeduplicateActions_nullComponent() {
198     Bundle phoneExtras = new Bundle();
199     Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
200     ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
201     PendingIntent pendingIntent =
202         PendingIntent.getActivity(ApplicationProvider.getApplicationContext(), 0, phoneIntent, 0);
203     Icon icon = Icon.createWithData(new byte[0], 0, 0);
204     ConversationAction action =
205         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
206             .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
207             .setExtras(phoneExtras)
208             .build();
209     ConversationAction actionWithSameLabel =
210         new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
211             .setAction(new RemoteAction(icon, "label", "2", pendingIntent))
212             .setExtras(phoneExtras)
213             .build();
214 
215     List<ConversationAction> conversationActions =
216         ActionsSuggestionsHelper.removeActionsWithDuplicates(
217             Arrays.asList(action, actionWithSameLabel));
218 
219     assertThat(conversationActions).isEmpty();
220   }
221 
222   @Test
createLabeledIntentResult_null()223   public void createLabeledIntentResult_null() {
224     ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
225         new ActionsSuggestionsModel.ActionSuggestion(
226             "text", ConversationAction.TYPE_OPEN_URL, 1.0f, null, null, null);
227 
228     LabeledIntent.Result labeledIntentResult =
229         ActionsSuggestionsHelper.createLabeledIntentResult(
230             ApplicationProvider.getApplicationContext(),
231             new TemplateIntentFactory(),
232             nativeSuggestion);
233 
234     assertThat(labeledIntentResult).isNull();
235   }
236 
237   @Test
createLabeledIntentResult_emptyList()238   public void createLabeledIntentResult_emptyList() {
239     ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
240         new ActionsSuggestionsModel.ActionSuggestion(
241             "text",
242             ConversationAction.TYPE_OPEN_URL,
243             1.0f,
244             null,
245             null,
246             new RemoteActionTemplate[0]);
247 
248     LabeledIntent.Result labeledIntentResult =
249         ActionsSuggestionsHelper.createLabeledIntentResult(
250             ApplicationProvider.getApplicationContext(),
251             new TemplateIntentFactory(),
252             nativeSuggestion);
253 
254     assertThat(labeledIntentResult).isNull();
255   }
256 
257   @Test
createLabeledIntentResult()258   public void createLabeledIntentResult() {
259     ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
260         new ActionsSuggestionsModel.ActionSuggestion(
261             "text",
262             ConversationAction.TYPE_OPEN_URL,
263             1.0f,
264             null,
265             null,
266             new RemoteActionTemplate[] {
267               new RemoteActionTemplate(
268                   "title",
269                   null,
270                   "description",
271                   null,
272                   Intent.ACTION_VIEW,
273                   Uri.parse("http://www.android.com").toString(),
274                   null,
275                   0,
276                   null,
277                   null,
278                   null,
279                   0)
280             });
281 
282     LabeledIntent.Result labeledIntentResult =
283         ActionsSuggestionsHelper.createLabeledIntentResult(
284             ApplicationProvider.getApplicationContext(),
285             new TemplateIntentFactory(),
286             nativeSuggestion);
287 
288     assertThat(labeledIntentResult.remoteAction.getTitle().toString()).isEqualTo("title");
289     assertThat(labeledIntentResult.resolvedIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
290   }
291 
createZonedDateTimeFromMsUtc(long msUtc)292   private static ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
293     return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
294   }
295 
assertNativeMessage( ActionsSuggestionsModel.ConversationMessage nativeMessage, CharSequence text, int userId, long referenceTimeInMsUtc)296   private static void assertNativeMessage(
297       ActionsSuggestionsModel.ConversationMessage nativeMessage,
298       CharSequence text,
299       int userId,
300       long referenceTimeInMsUtc) {
301     assertThat(nativeMessage.getText()).isEqualTo(text.toString());
302     assertThat(nativeMessage.getUserId()).isEqualTo(userId);
303     assertThat(nativeMessage.getDetectedTextLanguageTags()).isEqualTo(LOCALE_TAG);
304     assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
305   }
306 }
307