剪切板主要框架
官方给出的剪切板主要框架:
从 Android 13 开始,将内容添加到剪贴板时,系统会显示标准视觉确认界面。新确认界面会执行以下操作:
- 确认内容已成功复制。
- 提供所复制内容的预览。
下面看看实现方式:
新增功能主要流程分析
通过查看搜索,发现剪切板弹框出现在systemuiapp中,源码的位置:frameworks/base/packages/SystemUI/src/com/android/systemui/clipboardoverlay/
。
可以看到该目录下存在几个文件:
ClipboardListener.java //用来监听剪切板事件
ClipboardOverlayController.java //控制UI显示
ClipboardOverlayControllerFactory.java //控制UI显示工厂类
ClipboardOverlayEvent.java //显示消息
EditTextActivity.java //编辑界面
当剪切板变化时候,ClipBoradListener中会监听到剪切板事件,并回调到Listener中的onPrimaryClipChanged事件:
public void onPrimaryClipChanged() {
if (!mClipboardManager.hasPrimaryClip()) {
return;
}
String clipSource = mClipboardManager.getPrimaryClipSource();
ClipData clipData = mClipboardManager.getPrimaryClip();
if (shouldSuppressOverlay(clipData, clipSource, isEmulator())) {
Log.i(TAG, "Clipboard overlay suppressed.");
return;
}
if (mClipboardOverlayController == null) {
mClipboardOverlayController = mOverlayFactory.create(mContext);
mUiEventLogger.log(CLIPBOARD_OVERLAY_ENTERED, 0, clipSource);
} else {
mUiEventLogger.log(CLIPBOARD_OVERLAY_UPDATED, 0, clipSource);
}
mClipboardOverlayController.setClipData(clipData, clipSource);
mClipboardOverlayController.setOnSessionCompleteListener(() -> {
// Session is complete, free memory until it's needed again.
mClipboardOverlayController = null;
});
}
可以看到,对支持Overlay的类型,都会走到ClipboardOverlayController的处理中,这里主要看下setClipData的处理:
void setClipData(ClipData clipData, String clipSource) {
....
if (clipData == null || clipData.getItemCount() == 0) {
showTextPreview(
mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
mTextPreview);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
} else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
ClipData.Item item = clipData.getItemAt(0);
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
if (item.getTextLinks() != null) {
AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
}
}
if (isSensitive) {
showEditableText(
mContext.getResources().getString(R.string.clipboard_asterisks), true);
} else {
showEditableText(item.getText(), false);
}
showShareChip(clipData);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_text_copied);
} else if (clipData.getItemAt(0).getUri() != null) {
if (tryShowEditableImage(clipData.getItemAt(0).getUri(), isSensitive)) {
showShareChip(clipData);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_image_copied);
} else {
accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
}
} else {
showTextPreview(
mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
mTextPreview);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
}
Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
// Only show remote copy if it's available.
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.resolveActivity(
remoteCopyIntent, PackageManager.ResolveInfoFlags.of(0)) != null) {
mRemoteCopyChip.setContentDescription(
mContext.getString(R.string.clipboard_send_nearby_description));
mRemoteCopyChip.setVisibility(View.VISIBLE);
mRemoteCopyChip.setOnClickListener((v) -> {
mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
mContext.startActivity(remoteCopyIntent);
animateOut();
});
mActionContainerBackground.setVisibility(View.VISIBLE);
} else {
mRemoteCopyChip.setVisibility(View.GONE);
}
....
}
敏感行为的应用
如果允许用户将敏感内容(例如密码或信用卡信息)复制到剪贴板,则您必须先在 ClipData 中的 ClipDescription 中添加一个标志,然后再调用 ClipboardManager.setPrimaryClip()。
// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
}
// If your app is compiled with a lower SDK.
clipData.apply {
description.extras = PersistableBundle().apply {
putBoolean("android.content.extra.IS_SENSITIVE", true)
}
}
具体效果如下:
(设置/未设置)