1 /*
2  * Copyright (C) 2022 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.systemui.clipboardoverlay;
18 
19 import android.app.RemoteAction;
20 import android.content.ClipData;
21 import android.content.ClipDescription;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.os.Build;
25 import android.provider.DeviceConfig;
26 import android.text.TextUtils;
27 import android.view.textclassifier.TextClassification;
28 import android.view.textclassifier.TextClassificationManager;
29 import android.view.textclassifier.TextClassifier;
30 import android.view.textclassifier.TextLinks;
31 
32 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
33 import com.android.systemui.res.R;
34 
35 import java.util.ArrayList;
36 import java.util.Optional;
37 
38 import javax.inject.Inject;
39 
40 class ClipboardOverlayUtils {
41 
42     // minimum proportion of entire text an entity must take up, to be considered for smart actions
43     private static final float MINIMUM_ENTITY_PROPORTION = .8f;
44 
45     private final TextClassifier mTextClassifier;
46 
47     @Inject
ClipboardOverlayUtils(TextClassificationManager textClassificationManager)48     ClipboardOverlayUtils(TextClassificationManager textClassificationManager) {
49         mTextClassifier = textClassificationManager.getTextClassifier();
50     }
51 
isRemoteCopy(Context context, ClipData clipData, String clipSource)52     boolean isRemoteCopy(Context context, ClipData clipData, String clipSource) {
53         if (clipData != null && clipData.getDescription().getExtras() != null
54                 && clipData.getDescription().getExtras().getBoolean(
55                 ClipDescription.EXTRA_IS_REMOTE_DEVICE)) {
56             if (Build.isDebuggable() && DeviceConfig.getBoolean(
57                     DeviceConfig.NAMESPACE_SYSTEMUI,
58                     SystemUiDeviceConfigFlags.CLIPBOARD_IGNORE_REMOTE_COPY_SOURCE,
59                     false)) {
60                 return true;
61             }
62             ComponentName remoteComponent = ComponentName.unflattenFromString(
63                     context.getResources().getString(R.string.config_remoteCopyPackage));
64             if (remoteComponent != null) {
65                 return remoteComponent.getPackageName().equals(clipSource);
66             }
67         }
68         return false;
69     }
70 
getAction(TextLinks textLinks, String source)71     public Optional<RemoteAction> getAction(TextLinks textLinks, String source) {
72         return getActions(textLinks).stream().filter(remoteAction -> {
73             ComponentName component = remoteAction.getActionIntent().getIntent().getComponent();
74             return component != null && !TextUtils.equals(source, component.getPackageName());
75         }).findFirst();
76     }
77 
getActions(TextLinks textLinks)78     private ArrayList<RemoteAction> getActions(TextLinks textLinks) {
79         ArrayList<RemoteAction> actions = new ArrayList<>();
80         for (TextLinks.TextLink link : textLinks.getLinks()) {
81             // skip classification for incidental entities
82             if (link.getEnd() - link.getStart()
83                     >= textLinks.getText().length() * MINIMUM_ENTITY_PROPORTION) {
84                 TextClassification classification = mTextClassifier.classifyText(
85                         textLinks.getText(), link.getStart(), link.getEnd(), null);
86                 actions.addAll(classification.getActions());
87             }
88         }
89         return actions;
90     }
91 }
92