• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 android.content.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.fail;
22 
23 import android.app.Activity;
24 import android.content.ClipData;
25 import android.content.ClipDescription;
26 import android.content.ClipboardManager;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.net.Uri;
30 import android.os.Process;
31 import android.platform.test.annotations.AppModeNonSdkSandbox;
32 import android.platform.test.annotations.AppModeSdkSandbox;
33 import android.platform.test.annotations.DisabledOnRavenwood;
34 import android.platform.test.ravenwood.RavenwoodRule;
35 import android.text.SpannableString;
36 import android.text.style.UnderlineSpan;
37 import android.view.textclassifier.TextClassifier;
38 import android.view.textclassifier.TextLinks;
39 
40 import androidx.test.InstrumentationRegistry;
41 import androidx.test.annotation.UiThreadTest;
42 import androidx.test.filters.SmallTest;
43 import androidx.test.runner.AndroidJUnit4;
44 import androidx.test.uiautomator.By;
45 import androidx.test.uiautomator.UiDevice;
46 import androidx.test.uiautomator.Until;
47 
48 import com.google.common.collect.Range;
49 
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.util.Calendar;
55 
56 /**
57  * To run:
58  * cts-tradefed run singleCommand cts-dev -m CtsContentTestCases -t android.content.cts.ClipDescriptionTest
59  */
60 @SmallTest
61 @RunWith(AndroidJUnit4.class)
62 //@AppModeFull // TODO(Instant) Should clip board data be visible?
63 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
64 public class ClipDescriptionTest {
65     private UiDevice mUiDevice;
66     private Context mContext;
67 
68     private static final String SIXTY_CHARS =
69             "Hello this is a string of sixty characters in length exactly";
70 
71     @Before
setUp()72     public void setUp() throws Exception {
73         if (RavenwoodRule.isOnRavenwood() || Process.isSdkSandbox()) return;
74 
75         mContext = InstrumentationRegistry.getTargetContext();
76         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
77         mUiDevice.wakeUp();
78 
79         // Clear any dialogs and launch an activity as focus is needed to access clipboard.
80         mUiDevice.pressHome();
81         mUiDevice.pressBack();
82         launchActivity(MockActivity.class);
83     }
84 
85     @UiThreadTest
86     @Test
87     @DisabledOnRavenwood(blockedBy = ClipboardManager.class)
88     @AppModeNonSdkSandbox(reason = "SDK sandboxes cannot access ClipboardManager.")
testGetTimestamp()89     public void testGetTimestamp() {
90         final ClipboardManager clipboardManager = (ClipboardManager)
91                 InstrumentationRegistry.getTargetContext().getSystemService(
92                         Context.CLIPBOARD_SERVICE);
93         final long timestampBeforeSet = System.currentTimeMillis();
94         clipboardManager.setPrimaryClip(ClipData.newPlainText("Dummy text", "Text"));
95         final long timestampAfterSet = System.currentTimeMillis();
96         final long timestamp = clipboardManager.getPrimaryClipDescription().getTimestamp();
97         if (timestamp < timestampBeforeSet || timestamp > timestampAfterSet) {
98             fail("Value of timestamp is not as expected.\n"
99                     + "timestamp before setting clip: " + logTime(timestampBeforeSet) + "\n"
100                     + "timestamp after setting clip: " + logTime(timestampAfterSet) + "\n"
101                     + "actual timestamp: " + logTime(timestamp) + "\n"
102                     + "clipdata: " + clipboardManager.getPrimaryClip());
103         }
104     }
105 
106     @Test
107     @DisabledOnRavenwood(blockedBy = ClipboardManager.class)
108     @AppModeNonSdkSandbox(reason = "SDK sandboxes cannot access ClipboardManager.")
testIsStyledText()109     public void testIsStyledText() {
110         ClipDescription clipDescription = new ClipDescription(
111                 "label", new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN });
112 
113         // False as clip description is not attached to anything.
114         assertThat(clipDescription.isStyledText()).isFalse();
115 
116         SpannableString spannable = new SpannableString("Hello this is some text");
117         spannable.setSpan(new UnderlineSpan(), 6, 10, 0);
118         ClipData clipData = new ClipData(clipDescription, new ClipData.Item(spannable));
119 
120         assertThat(clipDescription.isStyledText()).isTrue();
121 
122         ClipboardManager clipboardManager = (ClipboardManager)
123                 InstrumentationRegistry.getTargetContext().getSystemService(
124                         Context.CLIPBOARD_SERVICE);
125         clipboardManager.setPrimaryClip(clipData);
126         assertThat(clipboardManager.getPrimaryClipDescription().isStyledText()).isTrue();
127 
128         ClipData clipData2 = ClipData.newPlainText("label", spannable);
129         assertThat(clipData2.getDescription().isStyledText()).isTrue();
130         clipboardManager.setPrimaryClip(clipData2);
131         assertThat(clipboardManager.getPrimaryClipDescription().isStyledText()).isTrue();
132     }
133 
134     @Test
135     @DisabledOnRavenwood(blockedBy = ClipboardManager.class)
136     @AppModeNonSdkSandbox(reason = "SDK sandboxes cannot access ClipboardManager.")
testNotStyledText()137     public void testNotStyledText() {
138         ClipDescription clipDescription = new ClipDescription(
139                 "label", new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN });
140         ClipData clipData = new ClipData(clipDescription, new ClipData.Item("Just text"));
141         assertThat(clipDescription.isStyledText()).isFalse();
142 
143         ClipboardManager clipboardManager = (ClipboardManager)
144                 InstrumentationRegistry.getTargetContext().getSystemService(
145                         Context.CLIPBOARD_SERVICE);
146         clipboardManager.setPrimaryClip(clipData);
147         assertThat(clipboardManager.getPrimaryClipDescription().isStyledText()).isFalse();
148 
149         ClipData clipData2 = ClipData.newPlainText("label", "Just some text");
150         assertThat(clipData2.getDescription().isStyledText()).isFalse();
151         clipboardManager.setPrimaryClip(clipData2);
152         assertThat(clipboardManager.getPrimaryClipDescription().isStyledText()).isFalse();
153 
154         // Test that a URI is not considered styled text.
155         ClipData clipDataUri = ClipData.newRawUri("label", Uri.parse("content://test"));
156         assertThat(clipDataUri.getDescription().isStyledText()).isFalse();
157         clipboardManager.setPrimaryClip(clipDataUri);
158         assertThat(clipboardManager.getPrimaryClipDescription().isStyledText()).isFalse();
159     }
160 
161     @Test
testClassificationNotCompletedBeforeCopy()162     public void testClassificationNotCompletedBeforeCopy() {
163         ClipDescription clipDescription = new ClipDescription(
164                 "label", new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN });
165         assertThat(clipDescription.getClassificationStatus())
166                 .isEqualTo(ClipDescription.CLASSIFICATION_NOT_COMPLETE);
167 
168         ClipData clipData = ClipData.newPlainText("label", "Just some text");
169         assertThat(clipDescription.getClassificationStatus())
170                 .isEqualTo(ClipDescription.CLASSIFICATION_NOT_COMPLETE);
171     }
172 
173     @Test
174     @DisabledOnRavenwood(blockedBy = ClipboardManager.class)
175     @AppModeNonSdkSandbox(reason = "SDK sandboxes cannot access ClipboardManager.")
testClassificationNotPerformedForVeryLongText()176     public void testClassificationNotPerformedForVeryLongText() {
177         StringBuilder builder = new StringBuilder();
178         for (int i = 0; i < 100; i++) {
179             builder.append(SIXTY_CHARS);
180         }
181         String longString = builder.toString();
182         ClipData clipData = ClipData.newPlainText("label", longString);
183 
184         ClipboardManager clipboardManager = (ClipboardManager)
185                 InstrumentationRegistry.getTargetContext().getSystemService(
186                         Context.CLIPBOARD_SERVICE);
187         clipboardManager.setPrimaryClip(clipData);
188         ClipDescription description = clipboardManager.getPrimaryClipDescription();
189         assertThat(description.getClassificationStatus())
190                 .isEqualTo(ClipDescription.CLASSIFICATION_NOT_PERFORMED);
191         assertThat(clipboardManager.getPrimaryClip().getItemAt(0).getTextLinks())
192                 .isNull();
193     }
194 
195     @Test
196     @DisabledOnRavenwood(blockedBy = ClipboardManager.class)
197     @AppModeNonSdkSandbox(reason = "SDK sandboxes cannot access ClipboardManager.")
testClassificationConfidenceValuesAreValid()198     public void testClassificationConfidenceValuesAreValid() throws InterruptedException {
199         ClipData clipData = ClipData.newPlainText(
200                 "label", "Hi Sam, try www.android.com on 05/04/2021 then visit "
201                         + "221B Baker Street, London and email test@example.com");
202         ClipboardManager clipboardManager = (ClipboardManager)
203                 InstrumentationRegistry.getTargetContext().getSystemService(
204                         Context.CLIPBOARD_SERVICE);
205         clipboardManager.setPrimaryClip(clipData);
206 
207         ClipDescription description = clipboardManager.getPrimaryClipDescription();
208         for (int i = 0; i < 10; i++) {
209             description = clipboardManager.getPrimaryClipDescription();
210             if (description.getClassificationStatus()
211                     != ClipDescription.CLASSIFICATION_NOT_COMPLETE) {
212                 break;
213             }
214             Thread.sleep(1000);
215         }
216 
217         if (description.getClassificationStatus() == ClipDescription.CLASSIFICATION_NOT_PERFORMED) {
218             return;
219         }
220 
221         float urlConfidence = description.getConfidenceScore(TextClassifier.TYPE_URL);
222         float dateConfidence = description.getConfidenceScore(TextClassifier.TYPE_DATE);
223         float addressConfidence = description.getConfidenceScore(TextClassifier.TYPE_ADDRESS);
224         float emailConfidence = description.getConfidenceScore(TextClassifier.TYPE_EMAIL);
225 
226         assertThat(urlConfidence).isIn(Range.closed(0f, 1f));
227         assertThat(dateConfidence).isIn(Range.closed(0f, 1f));
228         assertThat(addressConfidence).isIn(Range.closed(0f, 1f));
229         assertThat(emailConfidence).isIn(Range.closed(0f, 1f));
230 
231         if (urlConfidence > 0 || dateConfidence > 0 || addressConfidence > 0
232                 || emailConfidence > 0) {
233             TextLinks textLinks =
234                     clipboardManager.getPrimaryClip().getItemAt(0).getTextLinks();
235             assertThat(getHighestConfidence(TextClassifier.TYPE_URL, textLinks))
236                     .isEqualTo(urlConfidence);
237             assertThat(getHighestConfidence(TextClassifier.TYPE_DATE, textLinks))
238                     .isEqualTo(dateConfidence);
239             assertThat(getHighestConfidence(TextClassifier.TYPE_ADDRESS, textLinks))
240                     .isEqualTo(addressConfidence);
241             assertThat(getHighestConfidence(TextClassifier.TYPE_EMAIL, textLinks))
242                     .isEqualTo(emailConfidence);
243         }
244     }
245 
getHighestConfidence(String entity, TextLinks textLinks)246     private float getHighestConfidence(String entity, TextLinks textLinks) {
247         float result = 0;
248         for (TextLinks.TextLink textLink : textLinks.getLinks()) {
249             result = Math.max(result, textLink.getConfidenceScore(entity));
250         }
251         return result;
252     }
253 
254     /**
255      * Convert a System.currentTimeMillis() value to a time of day value like
256      * that printed in logs. MM-DD-YY HH:MM:SS.MMM
257      *
258      * @param millis since the epoch (1/1/1970)
259      * @return String representation of the time.
260      */
logTime(long millis)261     public static String logTime(long millis) {
262         Calendar c = Calendar.getInstance();
263         if (millis >= 0) {
264             c.setTimeInMillis(millis);
265             return String.format("%tm-%td-%ty %tH:%tM:%tS.%tL", c, c, c, c, c, c, c);
266         } else {
267             return Long.toString(millis);
268         }
269     }
270 
launchActivity(Class<? extends Activity> clazz)271     private void launchActivity(Class<? extends Activity> clazz) {
272         Intent intent = new Intent(Intent.ACTION_MAIN);
273         intent.setClassName(mContext.getPackageName(), clazz.getName());
274         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
275         mContext.startActivity(intent);
276         mUiDevice.wait(Until.hasObject(By.pkg(clazz.getPackageName())), 15000);
277     }
278 }
279