Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a17dc87c authored by Matt Casey's avatar Matt Casey Committed by Android (Google) Code Review
Browse files

Merge "Fork clipboard overlay's IntentCreator behind a flag." into main

parents 73f896b8 8324973d
Loading
Loading
Loading
Loading
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.clipboardoverlay

import android.content.ClipData
import android.content.ComponentName
import android.content.Intent
import android.net.Uri
import android.text.SpannableString
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
    val creator = ActionIntentCreator()

    @Test
    fun test_getTextEditorIntent() {
        val intent = creator.getTextEditorIntent(context)
        assertEquals(ComponentName(context, EditTextActivity::class.java), intent.component)
        assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
    }

    @Test
    fun test_getRemoteCopyIntent() {
        context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "")

        val clipData = ClipData.newPlainText("Test", "Test Item")
        var intent = creator.getRemoteCopyIntent(clipData, context)

        assertEquals(null, intent.component)
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)
        assertEquals(clipData, intent.clipData)

        // Try again with a remote copy component
        val fakeComponent =
            ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
        context
            .getOrCreateTestableResources()
            .addOverride(R.string.config_remoteCopyPackage, fakeComponent.flattenToString())

        intent = creator.getRemoteCopyIntent(clipData, context)
        assertEquals(fakeComponent, intent.component)
    }

    @Test
    fun test_getImageEditIntent() {
        context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
        val fakeUri = Uri.parse("content://foo")
        var intent = creator.getImageEditIntent(fakeUri, context)

        assertEquals(Intent.ACTION_EDIT, intent.action)
        assertEquals("image/*", intent.type)
        assertEquals(null, intent.component)
        assertEquals("clipboard", intent.getStringExtra("edit_source"))
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)

        // try again with an editor component
        val fakeComponent =
            ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
        context
            .getOrCreateTestableResources()
            .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString())
        intent = creator.getImageEditIntent(fakeUri, context)
        assertEquals(fakeComponent, intent.component)
    }

    @Test
    fun test_getShareIntent_plaintext() {
        val clipData = ClipData.newPlainText("Test", "Test Item")
        val intent = creator.getShareIntent(clipData, context)

        assertEquals(Intent.ACTION_CHOOSER, intent.action)
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)
        val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
        assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
        assertEquals("text/plain", target?.type)
    }

    @Test
    fun test_getShareIntent_html() {
        val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>")
        val intent = creator.getShareIntent(clipData, getContext())

        assertEquals(Intent.ACTION_CHOOSER, intent.action)
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)
        val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
        assertEquals("Some HTML", target?.getStringExtra(Intent.EXTRA_TEXT))
        assertEquals("text/plain", target?.type)
    }

    @Test
    fun test_getShareIntent_image() {
        val uri = Uri.parse("content://something")
        val clipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(uri))
        val intent = creator.getShareIntent(clipData, context)

        assertEquals(Intent.ACTION_CHOOSER, intent.action)
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)
        val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
        assertEquals(uri, target?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
        assertEquals(uri, target?.clipData?.getItemAt(0)?.uri)
        assertEquals("image/png", target?.type)
    }

    @Test
    fun test_getShareIntent_spannableText() {
        val clipData = ClipData.newPlainText("Test", SpannableString("Test Item"))
        val intent = creator.getShareIntent(clipData, context)

        assertEquals(Intent.ACTION_CHOOSER, intent.action)
        assertFlags(intent, EXTERNAL_INTENT_FLAGS)
        val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
        assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
        assertEquals("text/plain", target?.type)
    }

    // Assert that the given flags are set
    private fun assertFlags(intent: Intent, flags: Int) {
        assertTrue((intent.flags and flags) == flags)
    }

    companion object {
        private const val EXTERNAL_INTENT_FLAGS: Int =
            (Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_CLEAR_TASK or
                Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
}
+12 −10
Original line number Diff line number Diff line
@@ -36,13 +36,15 @@ import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class IntentCreatorTest extends SysuiTestCase {
public class DefaultIntentCreatorTest extends SysuiTestCase {
    private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;

    private final DefaultIntentCreator mIntentCreator  = new DefaultIntentCreator();

    @Test
    public void test_getTextEditorIntent() {
        Intent intent = IntentCreator.getTextEditorIntent(getContext());
        Intent intent = mIntentCreator.getTextEditorIntent(getContext());
        assertEquals(new ComponentName(getContext(), EditTextActivity.class),
                intent.getComponent());
        assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -54,7 +56,7 @@ public class IntentCreatorTest extends SysuiTestCase {
                "");

        ClipData clipData = ClipData.newPlainText("Test", "Test Item");
        Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
        Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());

        assertEquals(null, intent.getComponent());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -66,7 +68,7 @@ public class IntentCreatorTest extends SysuiTestCase {
        getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
                fakeComponent.flattenToString());

        intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
        intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());
        assertEquals(fakeComponent, intent.getComponent());
    }

@@ -75,7 +77,7 @@ public class IntentCreatorTest extends SysuiTestCase {
        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                "");
        Uri fakeUri = Uri.parse("content://foo");
        Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
        Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());

        assertEquals(Intent.ACTION_EDIT, intent.getAction());
        assertEquals("image/*", intent.getType());
@@ -88,14 +90,14 @@ public class IntentCreatorTest extends SysuiTestCase {
                "com.android.remotecopy.RemoteCopyActivity");
        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                fakeComponent.flattenToString());
        intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
        intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
        assertEquals(fakeComponent, intent.getComponent());
    }

    @Test
    public void test_getShareIntent_plaintext() {
        ClipData clipData = ClipData.newPlainText("Test", "Test Item");
        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
        Intent intent = mIntentCreator.getShareIntent(clipData, getContext());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -108,7 +110,7 @@ public class IntentCreatorTest extends SysuiTestCase {
    public void test_getShareIntent_html() {
        ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
                "<b>Some HTML</b>");
        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
        Intent intent = mIntentCreator.getShareIntent(clipData, getContext());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -122,7 +124,7 @@ public class IntentCreatorTest extends SysuiTestCase {
        Uri uri = Uri.parse("content://something");
        ClipData clipData = new ClipData("Test", new String[]{"image/png"},
                new ClipData.Item(uri));
        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
        Intent intent = mIntentCreator.getShareIntent(clipData, getContext());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -135,7 +137,7 @@ public class IntentCreatorTest extends SysuiTestCase {
    @Test
    public void test_getShareIntent_spannableText() {
        ClipData clipData = ClipData.newPlainText("Test", new SpannableString("Test Item"));
        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
        Intent intent = mIntentCreator.getShareIntent(clipData, getContext());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+99 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.clipboardoverlay

import android.content.ClipData
import android.content.ClipDescription
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import javax.inject.Inject

@SysUISingleton
class ActionIntentCreator @Inject constructor() : IntentCreator {
    override fun getTextEditorIntent(context: Context?) =
        Intent(context, EditTextActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        }

    override fun getShareIntent(clipData: ClipData, context: Context?): Intent {
        val shareIntent = Intent(Intent.ACTION_SEND)

        // From the ACTION_SEND docs:
        //   "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
        //    MIME type of the data in EXTRA_STREAM"
        val uri = clipData.getItemAt(0).uri
        shareIntent.apply {
            if (uri != null) {
                // We don't use setData here because some apps interpret this as "to:".
                setType(clipData.description.getMimeType(0))
                // Include URI in ClipData also, so that grantPermission picks it up.
                setClipData(
                    ClipData(
                        ClipDescription("content", arrayOf(clipData.description.getMimeType(0))),
                        ClipData.Item(uri),
                    )
                )
                putExtra(Intent.EXTRA_STREAM, uri)
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            } else {
                putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString())
                setType("text/plain")
            }
        }

        return Intent.createChooser(shareIntent, null)
            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }

    override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
        val editorPackage = context.getString(R.string.config_screenshotEditor)
        return Intent(Intent.ACTION_EDIT).apply {
            if (!TextUtils.isEmpty(editorPackage)) {
                setComponent(ComponentName.unflattenFromString(editorPackage))
            }
            setDataAndType(uri, "image/*")
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
            putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD)
        }
    }

    override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
        val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
        return Intent(REMOTE_COPY_ACTION).apply {
            if (!TextUtils.isEmpty(remoteCopyPackage)) {
                setComponent(ComponentName.unflattenFromString(remoteCopyPackage))
            }

            setClipData(clipData)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        }
    }

    companion object {
        private const val EXTRA_EDIT_SOURCE: String = "edit_source"
        private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard"
        private const val REMOTE_COPY_ACTION: String = "android.intent.action.REMOTE_COPY"
    }
}
+16 −16
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.TimeoutHandler;

@@ -94,13 +93,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
    private final ClipboardOverlayWindow mWindow;
    private final TimeoutHandler mTimeoutHandler;
    private final ClipboardOverlayUtils mClipboardUtils;
    private final FeatureFlags mFeatureFlags;
    private final Executor mBgExecutor;
    private final ClipboardImageLoader mClipboardImageLoader;
    private final ClipboardTransitionExecutor mTransitionExecutor;

    private final ClipboardOverlayView mView;
    private final ClipboardIndicationProvider mClipboardIndicationProvider;
    private final IntentCreator mIntentCreator;

    private Runnable mOnSessionCompleteListener;
    private Runnable mOnRemoteCopyTapped;
@@ -189,13 +188,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
            BroadcastDispatcher broadcastDispatcher,
            BroadcastSender broadcastSender,
            TimeoutHandler timeoutHandler,
            FeatureFlags featureFlags,
            ClipboardOverlayUtils clipboardUtils,
            @Background Executor bgExecutor,
            ClipboardImageLoader clipboardImageLoader,
            ClipboardTransitionExecutor transitionExecutor,
            ClipboardIndicationProvider clipboardIndicationProvider,
            UiEventLogger uiEventLogger) {
            UiEventLogger uiEventLogger,
            IntentCreator intentCreator) {
        mContext = context;
        mBroadcastDispatcher = broadcastDispatcher;
        mClipboardImageLoader = clipboardImageLoader;
@@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
        mClipboardIndicationProvider = clipboardIndicationProvider;

        mClipboardLogger = new ClipboardLogger(uiEventLogger);
        mIntentCreator = intentCreator;

        mView = clipboardOverlayView;
        mWindow = clipboardOverlayWindow;
@@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
            hideImmediate();
        });

        mFeatureFlags = featureFlags;
        mTimeoutHandler = timeoutHandler;
        mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);

@@ -508,7 +507,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
    }

    private void maybeShowRemoteCopy(ClipData clipData) {
        Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
        Intent remoteCopyIntent = mIntentCreator.getRemoteCopyIntent(clipData, mContext);

        // Only show remote copy if it's available.
        PackageManager packageManager = mContext.getPackageManager();
        if (packageManager.resolveActivity(
@@ -558,19 +558,19 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv

    private void editImage(Uri uri) {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
        mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
        mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
        animateOut();
    }

    private void editText() {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
        mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
        mContext.startActivity(mIntentCreator.getTextEditorIntent(mContext));
        animateOut();
    }

    private void shareContent(ClipData clip) {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
        mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
        mContext.startActivity(mIntentCreator.getShareIntent(clip, mContext));
        animateOut();
    }

@@ -717,22 +717,22 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
    public void onRemoteCopyButtonTapped() {
        if (clipboardSharedTransitions()) {
            finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED,
                    IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
                    mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
        }
    }

    @Override
    public void onShareButtonTapped() {
        if (clipboardSharedTransitions()) {
            Intent shareIntent =
                    mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext);
            switch (mClipboardModel.getType()) {
                case TEXT:
                case URI:
                    finish(CLIPBOARD_OVERLAY_SHARE_TAPPED,
                            IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
                    finish(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
                    break;
                case IMAGE:
                    finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED,
                            IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
                    finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
                    break;
            }
        }
@@ -744,11 +744,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
            switch (mClipboardModel.getType()) {
                case TEXT:
                    finish(CLIPBOARD_OVERLAY_EDIT_TAPPED,
                            IntentCreator.getTextEditorIntent(mContext));
                            mIntentCreator.getTextEditorIntent(mContext));
                    break;
                case IMAGE:
                    finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
                            IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
                            mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
                    break;
                default:
                    Log.w(TAG, "Got preview tapped callback for non-editable type "
+102 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.clipboardoverlay;

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;

import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;

import javax.inject.Inject;

@SysUISingleton
public class DefaultIntentCreator implements IntentCreator {
    private static final String EXTRA_EDIT_SOURCE = "edit_source";
    private static final String EDIT_SOURCE_CLIPBOARD = "clipboard";
    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";

    @Inject
    public DefaultIntentCreator() {}

    public Intent getTextEditorIntent(Context context) {
        Intent intent = new Intent(context, EditTextActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        return intent;
    }

    public Intent getShareIntent(ClipData clipData, Context context) {
        Intent shareIntent = new Intent(Intent.ACTION_SEND);

        // From the ACTION_SEND docs:
        //   "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
        //    MIME type of the data in EXTRA_STREAM"
        Uri uri = clipData.getItemAt(0).getUri();
        if (uri != null) {
            // We don't use setData here because some apps interpret this as "to:".
            shareIntent.setType(clipData.getDescription().getMimeType(0));
            // Include URI in ClipData also, so that grantPermission picks it up.
            shareIntent.setClipData(new ClipData(
                    new ClipDescription(
                            "content", new String[]{clipData.getDescription().getMimeType(0)}),
                    new ClipData.Item(uri)));
            shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            shareIntent.putExtra(
                    Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString());
            shareIntent.setType("text/plain");
        }
        Intent chooserIntent = Intent.createChooser(shareIntent, null)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        return chooserIntent;
    }

    public Intent getImageEditIntent(Uri uri, Context context) {
        String editorPackage = context.getString(R.string.config_screenshotEditor);
        Intent editIntent = new Intent(Intent.ACTION_EDIT);
        if (!TextUtils.isEmpty(editorPackage)) {
            editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
        }
        editIntent.setDataAndType(uri, "image/*");
        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
        return editIntent;
    }

    public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
        Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);

        String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
        if (!TextUtils.isEmpty(remoteCopyPackage)) {
            nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
        }

        nearbyIntent.setClipData(clipData);
        nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        return nearbyIntent;
    }
}
Loading