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

Commit 24e0f524 authored by Matt Casey's avatar Matt Casey Committed by Automerger Merge Worker
Browse files

Merge "Fix MIME type of non-plain text share intents" into tm-qpr-dev am: 6118cfd1

parents 8e821bda 6118cfd1
Loading
Loading
Loading
Loading
+4 −43
Original line number Diff line number Diff line
@@ -119,15 +119,12 @@ import java.util.ArrayList;
 */
public class ClipboardOverlayController {
    private static final String TAG = "ClipboardOverlayCtrlr";
    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";

    /** Constants for screenshot/copy deconflicting */
    public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
    public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
    public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";

    private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";

    private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
    private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
    private static final int FONT_SEARCH_STEP_PX = 4;
@@ -383,7 +380,7 @@ public class ClipboardOverlayController {
                    mTextPreview);
            accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
        }
        Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
        Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
        // Only show remote copy if it's available.
        PackageManager packageManager = mContext.getPackageManager();
        if (packageManager.resolveActivity(
@@ -500,41 +497,19 @@ public class ClipboardOverlayController {

    private void editImage(Uri uri) {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
        String editorPackage = mContext.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_CLIPBOARD, true);
        mContext.startActivity(editIntent);
        mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
        animateOut();
    }

    private void editText() {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
        Intent editIntent = new Intent(mContext, EditTextActivity.class);
        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        mContext.startActivity(editIntent);
        mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
        animateOut();
    }

    private void shareContent(ClipData clip) {
        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setDataAndType(
                clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
        if (clip.getItemAt(0).getUri() != null) {
            shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        Intent chooserIntent = Intent.createChooser(shareIntent, null)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        mContext.startActivity(chooserIntent);
        mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
        animateOut();
    }

@@ -667,20 +642,6 @@ public class ClipboardOverlayController {
                mContext.getString(R.string.clipboard_edit), null);
    }

    private Intent getRemoteCopyIntent(ClipData clipData) {
        Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);

        String remoteCopyPackage = mContext.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;
    }

    private void animateIn() {
        if (mAccessibilityManager.isEnabled()) {
            mDismissButton.setVisibility(View.VISIBLE);
+86 −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.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;

import com.android.systemui.R;

class IntentCreator {
    private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";

    static 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;
    }

    static 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"
        if (clipData.getItemAt(0).getUri() != null) {
            shareIntent.setDataAndType(
                    clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0));
            shareIntent.putExtra(Intent.EXTRA_STREAM, clipData.getItemAt(0).getUri());
            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            shareIntent.putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context));
            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;
    }

    static 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_CLIPBOARD, true);
        return editIntent;
    }

    static 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;
    }
}
+137 −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.ComponentName;
import android.content.Intent;
import android.net.Uri;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class IntentCreatorTest 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;

    @Test
    public void test_getTextEditorIntent() {
        Intent intent = IntentCreator.getTextEditorIntent(getContext());
        assertEquals(new ComponentName(getContext(), EditTextActivity.class),
                intent.getComponent());
        assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    }

    @Test
    public void test_getRemoteCopyIntent() {
        getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
                "");

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

        assertEquals(null, intent.getComponent());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
        assertEquals(clipData, intent.getClipData());

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

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

    @Test
    public void test_getImageEditIntent() {
        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                "");
        Uri fakeUri = Uri.parse("content://foo");
        Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());

        assertEquals(Intent.ACTION_EDIT, intent.getAction());
        assertEquals("image/*", intent.getType());
        assertEquals(null, intent.getComponent());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);

        // try again with an editor component
        ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
                "com.android.remotecopy.RemoteCopyActivity");
        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                fakeComponent.flattenToString());
        intent = IntentCreator.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());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
        assertEquals("Test Item", target.getStringExtra(Intent.EXTRA_TEXT));
        assertEquals("text/plain", target.getType());
    }

    @Test
    public void test_getShareIntent_html() {
        ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
                "<b>Some HTML</b>");
        Intent intent = IntentCreator.getShareIntent(clipData, getContext());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
        assertEquals("Some HTML", target.getStringExtra(Intent.EXTRA_TEXT));
        assertEquals("text/plain", target.getType());
    }

    @Test
    public void test_getShareIntent_image() {
        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());

        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
        assertEquals(uri, target.getData());
        assertEquals("image/png", target.getType());
    }

    // Assert that the given flags are set
    private void assertFlags(Intent intent, int flags) {
        assertTrue((intent.getFlags() & flags) == flags);
    }

}