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

Commit f38e9d25 authored by Matt Pietal's avatar Matt Pietal
Browse files

Sharesheet - Support sharing from downloads

Don't assume that all shared files have a DISPLAY_NAME index.
Downloads provide a different title index, so check the cursor
index for -1 values. Also make sure to close the cursor when done.

Bug: 124448157
Test: atest ChooserActivityTest, manually test sharing a download
Change-Id: I0beda6b72fb74c65fc4665c8234f147667925784
parent 753f4ce4
Loading
Loading
Loading
Loading
+28 −15
Original line number Original line Diff line number Diff line
@@ -72,6 +72,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract;
import android.provider.Downloads;
import android.provider.OpenableColumns;
import android.provider.OpenableColumns;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import android.service.chooser.ChooserTargetService;
@@ -620,28 +621,40 @@ public class ChooserActivity extends ResolverActivity {
        }
        }
    }
    }


    /**
     * Wrapping the ContentResolver call to expose for easier mocking,
     * and to avoid mocking Android core classes.
     */
    @VisibleForTesting
    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
        return resolver.query(uri, null, null, null, null);
    }

    private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
    private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
        String fileName = null;
        String fileName = null;
        boolean hasThumbnail = false;
        boolean hasThumbnail = false;
        Cursor cursor = null;

        try {
            cursor = resolver.query(uri, null, null, null, null);
        } catch (SecurityException e) {
            Log.w(TAG, "Error loading file preview", e);
        }


        try (Cursor cursor = queryResolver(resolver, uri)) {
            if (cursor != null && cursor.getCount() > 0) {
            if (cursor != null && cursor.getCount() > 0) {
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);
                int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
                int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);


                cursor.moveToFirst();
                cursor.moveToFirst();
                if (nameIndex != -1) {
                    fileName = cursor.getString(nameIndex);
                    fileName = cursor.getString(nameIndex);
                } else if (titleIndex != -1) {
                    fileName = cursor.getString(titleIndex);
                }

                if (flagsIndex != -1) {
                if (flagsIndex != -1) {
                    hasThumbnail = (cursor.getInt(flagsIndex)
                    hasThumbnail = (cursor.getInt(flagsIndex)
                            & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
                            & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
                }
                }
            }
            }
        } catch (SecurityException e) {
            Log.w(TAG, "Error loading file preview", e);
        }


        if (TextUtils.isEmpty(fileName)) {
        if (TextUtils.isEmpty(fileName)) {
            fileName = uri.getPath();
            fileName = uri.getPath();
+55 −0
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;
@@ -41,6 +42,7 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Color;
@@ -651,6 +653,59 @@ public class ChooserActivityTest {
        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
    }
    }


    @Test
    public void contentProviderThrowSecurityException() throws InterruptedException {
        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");

        ArrayList<Uri> uris = new ArrayList<>();
        uris.add(uri);

        Intent sendIntent = createSendUriIntentWithPreview(uris);

        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                Mockito.anyBoolean(),
                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);

        sOverrides.resolverForceException = true;

        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
        waitForIdle();
        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
    }

    @Test
    public void contentProviderReturnsNoColumns() throws InterruptedException {
        Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");

        ArrayList<Uri> uris = new ArrayList<>();
        uris.add(uri);
        uris.add(uri);

        Intent sendIntent = createSendUriIntentWithPreview(uris);

        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                Mockito.anyBoolean(),
                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);

        Cursor cursor = mock(Cursor.class);
        when(cursor.getCount()).thenReturn(1);
        Mockito.doNothing().when(cursor).close();
        when(cursor.moveToFirst()).thenReturn(true);
        when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1);

        sOverrides.resolverCursor = cursor;

        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
        waitForIdle();
        onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
        onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 1 file")));
        onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
    }

    private Intent createSendTextIntent() {
    private Intent createSendTextIntent() {
        Intent sendIntent = new Intent();
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.setAction(Intent.ACTION_SEND);
+19 −0
Original line number Original line Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.internal.app;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;


import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.Uri;
import android.util.Size;
import android.util.Size;
@@ -97,6 +99,19 @@ public class ChooserWrapperActivity extends ChooserActivity {
        return sOverrides.metricsLogger;
        return sOverrides.metricsLogger;
    }
    }


    @Override
    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
        if (sOverrides.resolverCursor != null) {
            return sOverrides.resolverCursor;
        }

        if (sOverrides.resolverForceException) {
            throw new SecurityException("Test exception handling");
        }

        return super.queryResolver(resolver, uri);
    }

    /**
    /**
     * We cannot directly mock the activity created since instrumentation creates it.
     * We cannot directly mock the activity created since instrumentation creates it.
     * <p>
     * <p>
@@ -109,6 +124,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
        public ResolverListController resolverListController;
        public ResolverListController resolverListController;
        public Boolean isVoiceInteraction;
        public Boolean isVoiceInteraction;
        public boolean isImageType;
        public boolean isImageType;
        public Cursor resolverCursor;
        public boolean resolverForceException;
        public Bitmap previewThumbnail;
        public Bitmap previewThumbnail;
        public MetricsLogger metricsLogger;
        public MetricsLogger metricsLogger;


@@ -118,6 +135,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
            createPackageManager = null;
            createPackageManager = null;
            previewThumbnail = null;
            previewThumbnail = null;
            isImageType = false;
            isImageType = false;
            resolverCursor = null;
            resolverForceException = false;
            resolverListController = mock(ResolverListController.class);
            resolverListController = mock(ResolverListController.class);
            metricsLogger = mock(MetricsLogger.class);
            metricsLogger = mock(MetricsLogger.class);
        }
        }