From 0c14c291b2d8474d2f061576ebe596ae071d4496 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 4 Oct 2022 04:02:22 +0500 Subject: [PATCH] Changed: Use `ShareUtils` to copy and paste text and prevent potential `NPE` The `copyTextToClipboard()` method has been updated to pass clip label when copying text to clipboard and `getTextFromClipboard()` and `getTextStringFromClipboardIfSet()` methods have been added to get current clipboard. --- .../TermuxTerminalSessionActivityClient.java | 12 ++-- .../terminal/TermuxTerminalViewClient.java | 13 ++-- .../java/com/termux/view/TerminalView.java | 11 +-- .../termux/shared/interact/ShareUtils.java | 72 ++++++++++++++++--- 4 files changed, 78 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java index e507cd192a..e3143635e9 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java @@ -183,20 +183,16 @@ public void onSessionFinished(@NonNull TerminalSession finishedSession) { public void onCopyTextToClipboard(@NonNull TerminalSession session, String text) { if (!mActivity.isVisible()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(text))); + ShareUtils.copyTextToClipboard(mActivity, text); } @Override public void onPasteTextFromClipboard(@Nullable TerminalSession session) { if (!mActivity.isVisible()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); - if (clipData != null) { - CharSequence paste = clipData.getItemAt(0).coerceToText(mActivity); - if (!TextUtils.isEmpty(paste)) mActivity.getTerminalView().mEmulator.paste(paste.toString()); - } + String text = ShareUtils.getTextStringFromClipboardIfSet(mActivity, true); + if (text != null) + mActivity.getTerminalView().mEmulator.paste(text); } @Override diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java index 9c38cdbc08..90350a9c1a 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java @@ -702,9 +702,7 @@ public void showUrlSelection() { // Click to copy url to clipboard: final AlertDialog dialog = new AlertDialog.Builder(mActivity).setItems(urls, (di, which) -> { String url = (String) urls[which]; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(url))); - Toast.makeText(mActivity, R.string.msg_select_url_copied_to_clipboard, Toast.LENGTH_LONG).show(); + ShareUtils.copyTextToClipboard(mActivity, url, mActivity.getString(R.string.msg_select_url_copied_to_clipboard)); }).setTitle(R.string.title_select_url_dialog).create(); // Long press to open URL: @@ -789,12 +787,9 @@ public void doPaste() { if (session == null) return; if (!session.isRunning()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); - if (clipData == null) return; - CharSequence paste = clipData.getItemAt(0).coerceToText(mActivity); - if (!TextUtils.isEmpty(paste)) - session.getEmulator().paste(paste.toString()); + String text = ShareUtils.getTextStringFromClipboardIfSet(mActivity, true); + if (text != null) + session.getEmulator().paste(text); } } diff --git a/terminal-view/src/main/java/com/termux/view/TerminalView.java b/terminal-view/src/main/java/com/termux/view/TerminalView.java index b96b0426b1..365e300001 100644 --- a/terminal-view/src/main/java/com/termux/view/TerminalView.java +++ b/terminal-view/src/main/java/com/termux/view/TerminalView.java @@ -566,11 +566,14 @@ public boolean onTouchEvent(MotionEvent event) { if (action == MotionEvent.ACTION_DOWN) showContextMenu(); return true; } else if (event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)) { - ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); + ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clipData = clipboardManager.getPrimaryClip(); if (clipData != null) { - CharSequence paste = clipData.getItemAt(0).coerceToText(getContext()); - if (!TextUtils.isEmpty(paste)) mEmulator.paste(paste.toString()); + ClipData.Item clipItem = clipData.getItemAt(0); + if (clipItem != null) { + CharSequence text = clipItem.coerceToText(getContext()); + if (!TextUtils.isEmpty(text)) mEmulator.paste(text.toString()); + } } } else if (mEmulator.isMouseTrackingActive()) { // BUTTON_PRIMARY. switch (event.getAction()) { diff --git a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java index 4d17af86f6..8d42215bcc 100644 --- a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java @@ -12,7 +12,6 @@ import android.os.Environment; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.content.ContextCompat; import com.termux.shared.R; import com.termux.shared.data.DataUtils; @@ -81,27 +80,82 @@ public static void shareText(final Context context, final String subject, final openSystemAppChooser(context, shareTextIntent, DataUtils.isNullOrEmpty(title) ? context.getString(R.string.title_share_with) : title); } + + + /** Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel` and `toastString`. */ + public static void copyTextToClipboard(Context context, final String text) { + copyTextToClipboard(context, null, text, null); + } + + /** Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel`. */ + public static void copyTextToClipboard(Context context, final String text, final String toastString) { + copyTextToClipboard(context, null, text, toastString); + } + /** - * Copy the text to clipboard. + * Copy the text to primary clip of the clipboard. * * @param context The context for operations. + * @param clipDataLabel The label to show to the user describing the copied text. * @param text The text to copy. * @param toastString If this is not {@code null} or empty, then a toast is shown if copying to * clipboard is successful. */ - public static void copyTextToClipboard(final Context context, final String text, final String toastString) { + public static void copyTextToClipboard(Context context, @Nullable final String clipDataLabel, + final String text, final String toastString) { if (context == null || text == null) return; - final ClipboardManager clipboardManager = ContextCompat.getSystemService(context, ClipboardManager.class); + ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboardManager == null) return; - if (clipboardManager != null) { - clipboardManager.setPrimaryClip(ClipData.newPlainText(null, DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, true, false, false))); - if (toastString != null && !toastString.isEmpty()) - Logger.showToast(context, toastString, true); - } + clipboardManager.setPrimaryClip(ClipData.newPlainText(clipDataLabel, + DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, + true, false, false))); + + if (toastString != null && !toastString.isEmpty()) + Logger.showToast(context, toastString, true); } + + /** + * Wrapper for {@link #getTextFromClipboard(Context, boolean)} that returns primary text {@link String} + * if its set and not empty. + */ + @Nullable + public static String getTextStringFromClipboardIfSet(Context context, boolean coerceToText) { + CharSequence textCharSequence = getTextFromClipboard(context, coerceToText); + if (textCharSequence == null) return null; + String textString = textCharSequence.toString(); + return !textString.isEmpty() ? textString : null; + } + + /** + * Get the text from primary clip of the clipboard. + * + * @param context The context for operations. + * @param coerceToText Whether to call {@link ClipData.Item#coerceToText(Context)} to coerce + * non-text data to text. + * @return Returns the {@link CharSequence} of primary text. This will be `null` if failed to get it. + */ + @Nullable + public static CharSequence getTextFromClipboard(Context context, boolean coerceToText) { + if (context == null) return null; + + ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboardManager == null) return null; + + ClipData clipData = clipboardManager.getPrimaryClip(); + if (clipData == null) return null; + + ClipData.Item clipItem = clipData.getItemAt(0); + if (clipItem == null) return null; + + return coerceToText ? clipItem.coerceToText(context) : clipItem.getText(); + } + + + * Open a url. * * @param context The context for operations.