/* * Copyright (C) 2015 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.messaging.ui.conversationlist; import android.app.Fragment; import android.content.ContentResolver; import android.content.Intent; import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import androidx.collection.ArrayMap; import com.android.messaging.Factory; import com.android.messaging.datamodel.data.ConversationListItemData; import com.android.messaging.datamodel.data.MessageData; import com.android.messaging.datamodel.data.PendingAttachmentData; import com.android.messaging.ui.BaseBugleActivity; import com.android.messaging.ui.UIIntents; import com.android.messaging.util.Assert; import com.android.messaging.util.ContentType; import com.android.messaging.util.LogUtil; import com.android.messaging.util.MediaMetadataRetrieverWrapper; import com.android.messaging.util.FileUtil; import com.android.messaging.util.UriUtil; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.util.ArrayList; import java.util.Map; public class ShareIntentActivity extends BaseBugleActivity implements ShareIntentFragment.HostInterface { private MessageData mDraftMessage; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent intent = getIntent(); if (Intent.ACTION_SEND.equals(intent.getAction()) && (!TextUtils.isEmpty(intent.getStringExtra("address")) || !TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_EMAIL)))) { // This is really more like a SENDTO intent because a destination is supplied. // It's coming through the SEND intent because that's the intent that is used // when invoking the chooser with Intent.createChooser(). final Intent convIntent = UIIntents.get().getLaunchConversationActivityIntent(this); // Copy the important items from the original intent to the new intent. convIntent.putExtras(intent); convIntent.setAction(Intent.ACTION_SENDTO); convIntent.setDataAndType(intent.getData(), intent.getType()); // We have to fire off the intent and finish before trying to show the fragment, // otherwise we get some flashing. startActivity(convIntent); finish(); return; } new ShareIntentFragment().show(getFragmentManager(), "ShareIntentFragment"); } @Override public void onAttachFragment(final Fragment fragment) { final Intent intent = getIntent(); final String action = intent.getAction(); String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT); if (sharedSubject == null) { sharedSubject = intent.getStringExtra(Intent.EXTRA_TITLE); } if (Intent.ACTION_SEND.equals(action)) { final Uri contentUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); if (UriUtil.isFileUri(contentUri)) { LogUtil.i( LogUtil.BUGLE_TAG, "Ignoring attachment from file URI which are no longer supported."); return; } final String contentType = extractContentType(contentUri, intent.getType()); if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.DEBUG)) { LogUtil.d(LogUtil.BUGLE_TAG, String.format( "onAttachFragment: contentUri=%s, intent.getType()=%s, inferredType=%s", contentUri, intent.getType(), contentType)); } if (ContentType.TEXT_PLAIN.equals(contentType)) { String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); if (sharedText == null) { // Try to get text string from content uri. sharedText = getTextStringFromContentUri(contentUri); } if (sharedText != null) { mDraftMessage = MessageData.createSharedMessage(sharedText, sharedSubject); } else { mDraftMessage = null; } } else if (PendingAttachmentData.isSupportedMediaType(contentType)) { if (contentUri != null) { mDraftMessage = MessageData.createSharedMessage(null, sharedSubject); addSharedPartToDraft(contentType, contentUri); } else { mDraftMessage = null; } } else { // Unsupported content type. LogUtil.e(LogUtil.BUGLE_TAG, "Unsupported shared content type for " + contentUri + ": " + contentType + " (" + intent.getType() + ")"); mDraftMessage = null; } } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { final String contentType = intent.getType(); // Handle sharing multiple contents. final ArrayList uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); if (uris != null && !uris.isEmpty()) { ArrayMap uriMap = new ArrayMap(); StringBuffer strBuffer = new StringBuffer(); for (final Uri uri : uris) { if (UriUtil.isFileUri(uri)) { LogUtil.i(LogUtil.BUGLE_TAG, "Ignoring attachment from file URI which are no longer supported."); continue; } final String actualContentType = extractContentType(uri, contentType); if (ContentType.TEXT_PLAIN.equals(actualContentType)) { // Try to get text string from content uri. String sharedText = getTextStringFromContentUri(uri); if (sharedText != null) { if (strBuffer.length() > 0) { strBuffer.append("\n"); } strBuffer.append(sharedText); } } else if (PendingAttachmentData.isSupportedMediaType(actualContentType)) { uriMap.put(uri, actualContentType); } else { // Unsupported content type. LogUtil.e(LogUtil.BUGLE_TAG, "Unsupported shared content type for " + uri + ": " + actualContentType); } } if (strBuffer.length() > 0 || !uriMap.isEmpty()) { mDraftMessage = MessageData.createSharedMessage(strBuffer.toString(), sharedSubject); for (final Map.Entry e : uriMap.entrySet()) { addSharedPartToDraft(e.getValue(), e.getKey()); } } } else { // No EXTRA_STREAM. LogUtil.e(LogUtil.BUGLE_TAG, "No shared URI."); mDraftMessage = null; } } else { // Unsupported action. Assert.fail("Unsupported action type for sharing: " + action); } } private static String getTextStringFromContentUri(final Uri contentUri) { if (contentUri == null) { return null; } final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); BufferedReader reader = null; try { final InputStream in = resolver.openInputStream(contentUri); reader = new BufferedReader(new InputStreamReader(in)); String line = reader.readLine(); if (line == null) { return null; } StringBuffer strBuffer = new StringBuffer(line); while ((line = reader.readLine()) != null) { strBuffer.append("\n").append(line); } return strBuffer.toString(); } catch (FileNotFoundException e) { LogUtil.w(LogUtil.BUGLE_TAG, "Can not find contentUri " + contentUri); } catch (IOException e) { LogUtil.w(LogUtil.BUGLE_TAG, "Can not read contentUri", e); } finally { try { if (reader != null) { reader.close(); } } catch (IOException e) { // Ignore } } return null; } private static String extractContentType(final Uri uri, final String contentType) { if (uri == null) { return contentType; } // First try looking at file extension. This is less reliable in some ways but it's // recommended by // https://developer.android.com/training/secure-file-sharing/retrieve-info.html // Some implementations of MediaMetadataRetriever get things horribly // wrong for common formats such as jpeg (reports as video/ffmpeg) final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver(); final String typeFromExtension = resolver.getType(uri); if (typeFromExtension != null) { return typeFromExtension; } final MediaMetadataRetrieverWrapper retriever = new MediaMetadataRetrieverWrapper(); try { retriever.setDataSource(uri); final String extractedType = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_MIMETYPE); if (extractedType != null) { return extractedType; } } catch (final IOException e) { LogUtil.i(LogUtil.BUGLE_TAG, "Could not determine type of " + uri, e); } finally { retriever.release(); } return contentType; } private void addSharedPartToDraft(final String contentType, final Uri uri) { if (FileUtil.isInPrivateDir(uri)) { Assert.fail("Cannot send private file " + uri.toString()); } else { mDraftMessage.addPart( PendingAttachmentData.createPendingAttachmentData(contentType, uri)); } } @Override public void onConversationClick(final ConversationListItemData conversationListItemData) { UIIntents.get().launchConversationActivity( this, conversationListItemData.getConversationId(), mDraftMessage); finish(); } @Override public void onCreateConversationClick() { UIIntents.get().launchCreateNewConversationActivity(this, mDraftMessage); finish(); } }