145 lines
6.2 KiB
Java
145 lines
6.2 KiB
Java
/*
|
|
* Copyright 2021 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.example.android.receivecontent;
|
|
|
|
import android.content.ClipDescription;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.res.AssetFileDescriptor;
|
|
import android.net.Uri;
|
|
import android.util.Log;
|
|
import android.util.Pair;
|
|
import android.view.ContentInfo;
|
|
import android.view.OnReceiveContentListener;
|
|
import android.view.View;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
import com.google.common.util.concurrent.FutureCallback;
|
|
import com.google.common.util.concurrent.Futures;
|
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
|
|
import java.io.FileNotFoundException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Sample {@link OnReceiveContentListener} implementation that accepts all URIs, and delegates
|
|
* handling for all other content to the platform.
|
|
*/
|
|
final class MyReceiver implements OnReceiveContentListener {
|
|
public static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
|
|
|
|
private final AttachmentsRepo mAttachmentsRepo;
|
|
private final AttachmentsRecyclerViewAdapter mAttachmentsRecyclerViewAdapter;
|
|
|
|
MyReceiver(@NonNull AttachmentsRepo attachmentsRepo,
|
|
@NonNull AttachmentsRecyclerViewAdapter attachmentsRecyclerViewAdapter) {
|
|
mAttachmentsRepo = attachmentsRepo;
|
|
mAttachmentsRecyclerViewAdapter = attachmentsRecyclerViewAdapter;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public ContentInfo onReceiveContent(@NonNull View view,
|
|
@NonNull ContentInfo contentInfo) {
|
|
// Split the incoming content into two groups: content URIs and everything else.
|
|
// This way we can implement custom handling for URIs and delegate the rest.
|
|
Pair<ContentInfo, ContentInfo> split = Utils.partition(contentInfo,
|
|
item -> item.getUri() != null);
|
|
ContentInfo uriContent = split.first;
|
|
ContentInfo remaining = split.second;
|
|
if (uriContent != null) {
|
|
receive(view.getContext(), uriContent);
|
|
}
|
|
// Return anything that we didn't handle ourselves. This preserves the default platform
|
|
// behavior for text and anything else for which we are not implementing custom handling.
|
|
return remaining;
|
|
}
|
|
|
|
/**
|
|
* Handles incoming content URIs. If the content is an image, stores it as an attachment in the
|
|
* app's private storage. If the content is any other type, simply shows a toast with the type
|
|
* of the content and its size in bytes.
|
|
*
|
|
* <p><strong>Important:</strong> It is significant that we pass along the {@code payload}
|
|
* object to the worker thread that will process the content, because URI permissions are tied
|
|
* to the payload object's lifecycle. If that object is not passed along, it could be garbage
|
|
* collected and permissions would be revoked prematurely (before we have a chance to process
|
|
* the content).
|
|
*/
|
|
private void receive(@NonNull Context context, @NonNull ContentInfo payload) {
|
|
Context applicationContext = context.getApplicationContext();
|
|
ContentResolver contentResolver = applicationContext.getContentResolver();
|
|
ListenableFuture<List<Uri>> addAttachmentsFuture = MyExecutors.bg().submit(() -> {
|
|
List<Uri> uris = Utils.collectUris(payload.getClip());
|
|
List<Uri> localUris = new ArrayList<>(uris.size());
|
|
for (Uri uri : uris) {
|
|
String mimeType = contentResolver.getType(uri);
|
|
Log.i(Logcat.TAG, "Processing " + mimeType + ": " + uri);
|
|
if (ClipDescription.compareMimeTypes(mimeType, "image/*")) {
|
|
// Read the image at the given URI and write it to private storage.
|
|
localUris.add(mAttachmentsRepo.write(uri));
|
|
} else {
|
|
showMessage(applicationContext, uri, mimeType);
|
|
}
|
|
}
|
|
return localUris;
|
|
});
|
|
Futures.addCallback(addAttachmentsFuture, new FutureCallback<List<Uri>>() {
|
|
@Override
|
|
public void onSuccess(List<Uri> localUris) {
|
|
// Show the image in the UI by passing the URI pointing to the locally stored copy
|
|
// to the recycler view adapter.
|
|
mAttachmentsRecyclerViewAdapter.addAttachments(localUris);
|
|
mAttachmentsRecyclerViewAdapter.notifyDataSetChanged();
|
|
Log.i(Logcat.TAG, "Processed content: " + payload);
|
|
}
|
|
@Override
|
|
public void onFailure(@NonNull Throwable t) {
|
|
Log.e(Logcat.TAG,"Error processing content: " + payload, t);
|
|
}
|
|
}, MyExecutors.main());
|
|
}
|
|
|
|
/**
|
|
* Reads the size of the given content URI and shows a toast with the type of the content and
|
|
* its size in bytes.
|
|
*/
|
|
private void showMessage(@NonNull Context applicationContext,
|
|
@NonNull Uri uri, @NonNull String mimeType) {
|
|
MyExecutors.bg().execute(() -> {
|
|
ContentResolver contentResolver = applicationContext.getContentResolver();
|
|
long lengthBytes;
|
|
try {
|
|
AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(uri, "r");
|
|
lengthBytes = fd.getLength();
|
|
} catch (FileNotFoundException e) {
|
|
Log.e(Logcat.TAG, "Error opening content URI: " + uri, e);
|
|
return;
|
|
}
|
|
String msg = "Received " + mimeType + " (" + lengthBytes + " bytes): " + uri;
|
|
Log.i(Logcat.TAG, msg);
|
|
MyExecutors.main().execute(() -> {
|
|
Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show();
|
|
});
|
|
});
|
|
}
|
|
}
|