|
|
|
|
@@ -28,6 +28,7 @@ import android.animation.Animator;
|
|
|
|
|
import android.animation.AnimatorListenerAdapter;
|
|
|
|
|
import android.animation.ValueAnimator;
|
|
|
|
|
import android.annotation.MainThread;
|
|
|
|
|
import android.app.RemoteAction;
|
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
|
import android.content.ClipData;
|
|
|
|
|
import android.content.ComponentName;
|
|
|
|
|
@@ -43,6 +44,7 @@ import android.graphics.drawable.Icon;
|
|
|
|
|
import android.hardware.display.DisplayManager;
|
|
|
|
|
import android.hardware.input.InputManager;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.AsyncTask;
|
|
|
|
|
import android.os.Looper;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
@@ -60,8 +62,13 @@ import android.view.ViewTreeObserver;
|
|
|
|
|
import android.view.WindowInsets;
|
|
|
|
|
import android.view.WindowManager;
|
|
|
|
|
import android.view.accessibility.AccessibilityManager;
|
|
|
|
|
import android.view.textclassifier.TextClassification;
|
|
|
|
|
import android.view.textclassifier.TextClassificationManager;
|
|
|
|
|
import android.view.textclassifier.TextClassifier;
|
|
|
|
|
import android.view.textclassifier.TextLinks;
|
|
|
|
|
import android.widget.FrameLayout;
|
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
|
|
|
|
|
import com.android.internal.policy.PhoneWindow;
|
|
|
|
|
@@ -71,6 +78,7 @@ import com.android.systemui.screenshot.ScreenshotActionChip;
|
|
|
|
|
import com.android.systemui.screenshot.TimeoutHandler;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Controls state and UI for the overlay that appears when something is added to the clipboard
|
|
|
|
|
@@ -93,6 +101,7 @@ public class ClipboardOverlayController {
|
|
|
|
|
private final PhoneWindow mWindow;
|
|
|
|
|
private final TimeoutHandler mTimeoutHandler;
|
|
|
|
|
private final AccessibilityManager mAccessibilityManager;
|
|
|
|
|
private final TextClassifier mTextClassifier;
|
|
|
|
|
|
|
|
|
|
private final DraggableConstraintLayout mView;
|
|
|
|
|
private final ImageView mImagePreview;
|
|
|
|
|
@@ -101,9 +110,13 @@ public class ClipboardOverlayController {
|
|
|
|
|
private final ScreenshotActionChip mRemoteCopyChip;
|
|
|
|
|
private final View mActionContainerBackground;
|
|
|
|
|
private final View mDismissButton;
|
|
|
|
|
private final LinearLayout mActionContainer;
|
|
|
|
|
private final ArrayList<ScreenshotActionChip> mActionChips = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
private Runnable mOnSessionCompleteListener;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private InputMonitor mInputMonitor;
|
|
|
|
|
private InputEventReceiver mInputEventReceiver;
|
|
|
|
|
|
|
|
|
|
private BroadcastReceiver mCloseDialogsReceiver;
|
|
|
|
|
@@ -117,6 +130,8 @@ public class ClipboardOverlayController {
|
|
|
|
|
mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
|
|
|
|
|
|
|
|
|
|
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
|
|
|
|
|
mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
|
|
|
|
|
.getTextClassifier();
|
|
|
|
|
|
|
|
|
|
mWindowManager = mContext.getSystemService(WindowManager.class);
|
|
|
|
|
|
|
|
|
|
@@ -134,8 +149,9 @@ public class ClipboardOverlayController {
|
|
|
|
|
|
|
|
|
|
mView = (DraggableConstraintLayout)
|
|
|
|
|
LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null);
|
|
|
|
|
mActionContainerBackground = requireNonNull(
|
|
|
|
|
mView.findViewById(R.id.actions_container_background));
|
|
|
|
|
mActionContainerBackground =
|
|
|
|
|
requireNonNull(mView.findViewById(R.id.actions_container_background));
|
|
|
|
|
mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
|
|
|
|
|
mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
|
|
|
|
|
mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
|
|
|
|
|
mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
|
|
|
|
|
@@ -143,7 +159,7 @@ public class ClipboardOverlayController {
|
|
|
|
|
mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
|
|
|
|
|
|
|
|
|
|
mView.setOnDismissCallback(this::hideImmediate);
|
|
|
|
|
mView.setOnInteractionCallback(() -> mTimeoutHandler.resetTimeout());
|
|
|
|
|
mView.setOnInteractionCallback(mTimeoutHandler::resetTimeout);
|
|
|
|
|
|
|
|
|
|
mDismissButton.setOnClickListener(view -> animateOut());
|
|
|
|
|
|
|
|
|
|
@@ -166,7 +182,7 @@ public class ClipboardOverlayController {
|
|
|
|
|
withWindowAttached(() -> {
|
|
|
|
|
mWindow.setContentView(mView);
|
|
|
|
|
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
|
|
|
|
|
mView.post(() -> getEnterAnimation().start());
|
|
|
|
|
mView.post(this::animateIn);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mTimeoutHandler.setOnTimeoutRunnable(this::animateOut);
|
|
|
|
|
@@ -199,12 +215,15 @@ public class ClipboardOverlayController {
|
|
|
|
|
|
|
|
|
|
void setClipData(ClipData clipData) {
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
|
|
if (clipData == null || clipData.getItemCount() == 0) {
|
|
|
|
|
showTextPreview(
|
|
|
|
|
mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
|
|
|
|
|
showTextPreview(mContext.getResources().getString(
|
|
|
|
|
R.string.clipboard_overlay_text_copied));
|
|
|
|
|
} else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
|
|
|
|
|
showEditableText(clipData.getItemAt(0).getText());
|
|
|
|
|
ClipData.Item item = clipData.getItemAt(0);
|
|
|
|
|
if (item.getTextLinks() != null) {
|
|
|
|
|
AsyncTask.execute(() -> classifyText(clipData.getItemAt(0)));
|
|
|
|
|
}
|
|
|
|
|
showEditableText(item.getText());
|
|
|
|
|
} else if (clipData.getItemAt(0).getUri() != null) {
|
|
|
|
|
// How to handle non-image URIs?
|
|
|
|
|
showEditableImage(clipData.getItemAt(0).getUri());
|
|
|
|
|
@@ -212,7 +231,6 @@ public class ClipboardOverlayController {
|
|
|
|
|
showTextPreview(
|
|
|
|
|
mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mTimeoutHandler.resetTimeout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -220,10 +238,40 @@ public class ClipboardOverlayController {
|
|
|
|
|
mOnSessionCompleteListener = runnable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void classifyText(ClipData.Item item) {
|
|
|
|
|
ArrayList<RemoteAction> actions = new ArrayList<>();
|
|
|
|
|
for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
|
|
|
|
|
TextClassification classification = mTextClassifier.classifyText(
|
|
|
|
|
item.getText(), link.getStart(), link.getEnd(), null);
|
|
|
|
|
actions.addAll(classification.getActions());
|
|
|
|
|
}
|
|
|
|
|
mView.post(() -> {
|
|
|
|
|
for (ScreenshotActionChip chip : mActionChips) {
|
|
|
|
|
mActionContainer.removeView(chip);
|
|
|
|
|
}
|
|
|
|
|
mActionChips.clear();
|
|
|
|
|
for (RemoteAction action : actions) {
|
|
|
|
|
ScreenshotActionChip chip = constructActionChip(action);
|
|
|
|
|
mActionContainer.addView(chip);
|
|
|
|
|
mActionChips.add(chip);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ScreenshotActionChip constructActionChip(RemoteAction action) {
|
|
|
|
|
ScreenshotActionChip chip = (ScreenshotActionChip) LayoutInflater.from(mContext).inflate(
|
|
|
|
|
R.layout.screenshot_action_chip, mActionContainer, false);
|
|
|
|
|
chip.setText(action.getTitle());
|
|
|
|
|
chip.setIcon(action.getIcon(), false);
|
|
|
|
|
chip.setPendingIntent(action.getActionIntent(), this::animateOut);
|
|
|
|
|
chip.setAlpha(1);
|
|
|
|
|
return chip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void monitorOutsideTouches() {
|
|
|
|
|
InputManager inputManager = mContext.getSystemService(InputManager.class);
|
|
|
|
|
InputMonitor monitor = inputManager.monitorGestureInput("clipboard overlay", 0);
|
|
|
|
|
mInputEventReceiver = new InputEventReceiver(monitor.getInputChannel(),
|
|
|
|
|
mInputMonitor = inputManager.monitorGestureInput("clipboard overlay", 0);
|
|
|
|
|
mInputEventReceiver = new InputEventReceiver(mInputMonitor.getInputChannel(),
|
|
|
|
|
Looper.getMainLooper()) {
|
|
|
|
|
@Override
|
|
|
|
|
public void onInputEvent(InputEvent event) {
|
|
|
|
|
@@ -311,6 +359,10 @@ public class ClipboardOverlayController {
|
|
|
|
|
return nearbyIntent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void animateIn() {
|
|
|
|
|
getEnterAnimation().start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void animateOut() {
|
|
|
|
|
getExitAnimation().start();
|
|
|
|
|
}
|
|
|
|
|
@@ -390,6 +442,10 @@ public class ClipboardOverlayController {
|
|
|
|
|
mInputEventReceiver.dispose();
|
|
|
|
|
mInputEventReceiver = null;
|
|
|
|
|
}
|
|
|
|
|
if (mInputMonitor != null) {
|
|
|
|
|
mInputMonitor.dispose();
|
|
|
|
|
mInputMonitor = null;
|
|
|
|
|
}
|
|
|
|
|
if (mOnSessionCompleteListener != null) {
|
|
|
|
|
mOnSessionCompleteListener.run();
|
|
|
|
|
}
|
|
|
|
|
@@ -398,6 +454,10 @@ public class ClipboardOverlayController {
|
|
|
|
|
private void reset() {
|
|
|
|
|
mView.setTranslationX(0);
|
|
|
|
|
mView.setAlpha(0);
|
|
|
|
|
for (ScreenshotActionChip chip : mActionChips) {
|
|
|
|
|
mActionContainer.removeView(chip);
|
|
|
|
|
}
|
|
|
|
|
mActionChips.clear();
|
|
|
|
|
mTimeoutHandler.cancelTimeout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|