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