summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Matt Casey <mrcasey@google.com> 2023-04-20 17:55:07 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-04-20 17:55:07 +0000
commit395868a876907174d4923823048bd1f81a1987a7 (patch)
tree21b86fd2e611ac10570a5f924b7b6eb42936fef1 /java/src
parent2b99e8fe664ff18349bad6bc66e5a3a45ca40ea7 (diff)
parent21cee78c93503c016f6d5cd16e8f5e24abf4910c (diff)
Merge "Files + text in a separate preview layout." into udc-dev
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java10
-rw-r--r--java/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUi.java193
-rw-r--r--java/src/com/android/intentresolver/contentpreview/HeadlineGenerator.kt6
-rw-r--r--java/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImpl.kt50
-rw-r--r--java/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUi.java74
5 files changed, 245 insertions, 88 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
index 181fe117..21930fdb 100644
--- a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
+++ b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
@@ -150,6 +150,15 @@ public final class ChooserContentPreviewUi {
}
ArrayList<FileInfo> files = new ArrayList<>(uris.size());
int previewCount = readFileInfo(contentResolver, typeClassifier, uris, files);
+ CharSequence text = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
+ if (!TextUtils.isEmpty(text)) {
+ return new FilesPlusTextContentPreviewUi(files,
+ targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT),
+ actionFactory,
+ imageLoader,
+ typeClassifier,
+ headlineGenerator);
+ }
if (previewCount == 0) {
return new FileContentPreviewUi(
files,
@@ -158,7 +167,6 @@ public final class ChooserContentPreviewUi {
}
return new UnifiedContentPreviewUi(
files,
- targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT),
actionFactory,
imageLoader,
typeClassifier,
diff --git a/java/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUi.java
new file mode 100644
index 00000000..5174234a
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUi.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 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.intentresolver.contentpreview;
+
+import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_FILE;
+import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE;
+
+import android.content.res.Resources;
+import android.text.util.Linkify;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.intentresolver.R;
+import com.android.intentresolver.widget.ActionRow;
+import com.android.intentresolver.widget.ScrollableImagePreviewView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * FilesPlusTextContentPreviewUi is shown when the user is sending 1 or more files along with
+ * non-empty EXTRA_TEXT. The text can be toggled with a checkbox. If a single image file is being
+ * shared, it is shown in a preview (otherwise the headline summary is the sole indication of the
+ * file content).
+ */
+class FilesPlusTextContentPreviewUi extends ContentPreviewUi {
+ private final List<FileInfo> mFiles;
+ private final CharSequence mText;
+ private final ChooserContentPreviewUi.ActionFactory mActionFactory;
+ private final ImageLoader mImageLoader;
+ private final MimeTypeClassifier mTypeClassifier;
+ private final HeadlineGenerator mHeadlineGenerator;
+ private final boolean mAllImages;
+ private final boolean mAllVideos;
+
+ FilesPlusTextContentPreviewUi(
+ List<FileInfo> files,
+ CharSequence text,
+ ChooserContentPreviewUi.ActionFactory actionFactory,
+ ImageLoader imageLoader,
+ MimeTypeClassifier typeClassifier,
+ HeadlineGenerator headlineGenerator) {
+ mFiles = files;
+ mText = text;
+ mActionFactory = actionFactory;
+ mImageLoader = imageLoader;
+ mTypeClassifier = typeClassifier;
+ mHeadlineGenerator = headlineGenerator;
+
+ boolean allImages = true;
+ boolean allVideos = true;
+ for (FileInfo fileInfo : mFiles) {
+ ScrollableImagePreviewView.PreviewType previewType =
+ getPreviewType(fileInfo.getMimeType());
+ allImages = allImages && previewType == ScrollableImagePreviewView.PreviewType.Image;
+ allVideos = allVideos && previewType == ScrollableImagePreviewView.PreviewType.Video;
+ }
+ mAllImages = allImages;
+ mAllVideos = allVideos;
+ }
+
+ @Override
+ public int getType() {
+ return shouldShowPreview() ? CONTENT_PREVIEW_IMAGE : CONTENT_PREVIEW_FILE;
+ }
+
+ @Override
+ public ViewGroup display(Resources resources, LayoutInflater layoutInflater, ViewGroup parent) {
+ ViewGroup layout = displayInternal(layoutInflater, parent);
+ displayModifyShareAction(layout, mActionFactory);
+ return layout;
+ }
+
+ private ViewGroup displayInternal(LayoutInflater layoutInflater, ViewGroup parent) {
+ ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
+ R.layout.chooser_grid_preview_files_text, parent, false);
+ ImageView imagePreview =
+ contentPreviewLayout.findViewById(R.id.image_view);
+
+ final ActionRow actionRow =
+ contentPreviewLayout.findViewById(com.android.internal.R.id.chooser_action_row);
+ actionRow.setActions(createActions(
+ createImagePreviewActions(),
+ mActionFactory.createCustomActions()));
+
+ if (shouldShowPreview()) {
+ mImageLoader.loadImage(mFiles.get(0).getPreviewUri(), bitmap -> {
+ if (bitmap == null) {
+ imagePreview.setVisibility(View.GONE);
+ } else {
+ imagePreview.setImageBitmap(bitmap);
+ }
+ });
+ } else {
+ imagePreview.setVisibility(View.GONE);
+ }
+
+ prepareTextPreview(contentPreviewLayout, mActionFactory);
+ updateHeadline(contentPreviewLayout);
+
+ return contentPreviewLayout;
+ }
+
+ private boolean shouldShowPreview() {
+ return mAllImages && mFiles.size() == 1 && mFiles.get(0).getPreviewUri() != null;
+ }
+
+ private List<ActionRow.Action> createImagePreviewActions() {
+ ArrayList<ActionRow.Action> actions = new ArrayList<>(2);
+ //TODO: add copy action;
+ if (mFiles.size() == 1 && mAllImages) {
+ ActionRow.Action action = mActionFactory.createEditButton();
+ if (action != null) {
+ actions.add(action);
+ }
+ }
+ return actions;
+ }
+
+ private void updateHeadline(ViewGroup contentPreview) {
+ CheckBox includeText = contentPreview.requireViewById(R.id.include_text_action);
+ String headline;
+ if (includeText.getVisibility() == View.VISIBLE && includeText.isChecked()) {
+ if (mAllImages) {
+ headline = mHeadlineGenerator.getImagesWithTextHeadline(mText, mFiles.size());
+ } else if (mAllVideos) {
+ headline = mHeadlineGenerator.getVideosWithTextHeadline(mText, mFiles.size());
+ } else {
+ headline = mHeadlineGenerator.getFilesWithTextHeadline(mText, mFiles.size());
+ }
+ } else {
+ if (mAllImages) {
+ headline = mHeadlineGenerator.getImagesHeadline(mFiles.size());
+ } else if (mAllVideos) {
+ headline = mHeadlineGenerator.getVideosHeadline(mFiles.size());
+ } else {
+ headline = mHeadlineGenerator.getItemsHeadline(mFiles.size());
+ }
+ }
+
+ displayHeadline(contentPreview, headline);
+ }
+
+ private void prepareTextPreview(
+ ViewGroup contentPreview,
+ ChooserContentPreviewUi.ActionFactory actionFactory) {
+ final TextView textView = contentPreview.requireViewById(R.id.content_preview_text);
+ CheckBox includeText = contentPreview.requireViewById(R.id.include_text_action);
+ boolean isLink = HttpUriMatcher.isHttpUri(mText.toString());
+ textView.setAutoLinkMask(isLink ? Linkify.WEB_URLS : 0);
+ textView.setText(mText);
+
+ final Consumer<Boolean> shareTextAction = actionFactory.getExcludeSharedTextAction();
+ includeText.setChecked(true);
+ includeText.setText(isLink ? R.string.include_link : R.string.include_text);
+ shareTextAction.accept(false);
+ includeText.setOnCheckedChangeListener((view, isChecked) -> {
+ textView.setEnabled(isChecked);
+ shareTextAction.accept(!isChecked);
+ updateHeadline(contentPreview);
+ });
+ includeText.setVisibility(View.VISIBLE);
+ }
+
+ private ScrollableImagePreviewView.PreviewType getPreviewType(String mimeType) {
+ if (mTypeClassifier.isImageType(mimeType)) {
+ return ScrollableImagePreviewView.PreviewType.Image;
+ }
+ if (mTypeClassifier.isVideoType(mimeType)) {
+ return ScrollableImagePreviewView.PreviewType.Video;
+ }
+ return ScrollableImagePreviewView.PreviewType.File;
+ }
+}
diff --git a/java/src/com/android/intentresolver/contentpreview/HeadlineGenerator.kt b/java/src/com/android/intentresolver/contentpreview/HeadlineGenerator.kt
index e32bb5c4..ad2a7ada 100644
--- a/java/src/com/android/intentresolver/contentpreview/HeadlineGenerator.kt
+++ b/java/src/com/android/intentresolver/contentpreview/HeadlineGenerator.kt
@@ -25,7 +25,11 @@ private const val PLURALS_COUNT = "count"
interface HeadlineGenerator {
fun getTextHeadline(text: CharSequence): String
- fun getImageWithTextHeadline(text: CharSequence): String
+ fun getImagesWithTextHeadline(text: CharSequence, count: Int): String
+
+ fun getVideosWithTextHeadline(text: CharSequence, count: Int): String
+
+ fun getFilesWithTextHeadline(text: CharSequence, count: Int): String
fun getImagesHeadline(count: Int): String
diff --git a/java/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImpl.kt b/java/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImpl.kt
index ae44294c..a6b782ad 100644
--- a/java/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImpl.kt
+++ b/java/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImpl.kt
@@ -16,6 +16,7 @@
package com.android.intentresolver.contentpreview
+import android.annotation.StringRes
import android.content.Context
import com.android.intentresolver.R
import android.util.PluralsMessageFormatter
@@ -28,40 +29,49 @@ private const val PLURALS_COUNT = "count"
*/
class HeadlineGeneratorImpl(private val context: Context) : HeadlineGenerator {
override fun getTextHeadline(text: CharSequence): String {
- if (text.toString().isHttpUri()) {
- return context.getString(R.string.sharing_link)
- }
- return context.getString(R.string.sharing_text)
+ return context.getString(
+ getTemplateResource(text, R.string.sharing_link, R.string.sharing_text))
}
- override fun getImageWithTextHeadline(text: CharSequence): String {
- if (text.toString().isHttpUri()) {
- return context.getString(R.string.sharing_image_with_link)
- }
- return context.getString(R.string.sharing_image_with_text)
+ override fun getImagesWithTextHeadline(text: CharSequence, count: Int): String {
+ return getPluralString(getTemplateResource(
+ text, R.string.sharing_images_with_link, R.string.sharing_images_with_text), count)
+ }
+
+ override fun getVideosWithTextHeadline(text: CharSequence, count: Int): String {
+ return getPluralString(getTemplateResource(
+ text, R.string.sharing_videos_with_link, R.string.sharing_videos_with_text), count)
+ }
+
+ override fun getFilesWithTextHeadline(text: CharSequence, count: Int): String {
+ return getPluralString(getTemplateResource(
+ text, R.string.sharing_files_with_link, R.string.sharing_files_with_text), count)
}
override fun getImagesHeadline(count: Int): String {
- return PluralsMessageFormatter.format(
- context.resources,
- mapOf(PLURALS_COUNT to count),
- R.string.sharing_images
- )
+ return getPluralString(R.string.sharing_images, count)
}
override fun getVideosHeadline(count: Int): String {
- return PluralsMessageFormatter.format(
- context.resources,
- mapOf(PLURALS_COUNT to count),
- R.string.sharing_videos
- )
+ return getPluralString(R.string.sharing_videos, count)
}
override fun getItemsHeadline(count: Int): String {
+ return getPluralString(R.string.sharing_items, count)
+ }
+
+ private fun getPluralString(@StringRes templateResource: Int, count: Int): String {
return PluralsMessageFormatter.format(
context.resources,
mapOf(PLURALS_COUNT to count),
- R.string.sharing_items
+ templateResource
)
}
+
+ @StringRes
+ private fun getTemplateResource(
+ text: CharSequence, @StringRes linkResource: Int, @StringRes nonLinkResource: Int
+ ): Int {
+ return if (text.toString().isHttpUri()) linkResource else nonLinkResource
+ }
}
diff --git a/java/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUi.java
index 709ec566..6f1be116 100644
--- a/java/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUi.java
+++ b/java/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUi.java
@@ -19,15 +19,10 @@ package com.android.intentresolver.contentpreview;
import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE;
import android.content.res.Resources;
-import android.text.TextUtils;
-import android.text.util.Linkify;
-import android.transition.TransitionManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.TextView;
import androidx.annotation.Nullable;
@@ -39,12 +34,10 @@ import com.android.intentresolver.widget.ScrollableImagePreviewView;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.function.Consumer;
class UnifiedContentPreviewUi extends ContentPreviewUi {
private final List<FileInfo> mFiles;
@Nullable
- private final CharSequence mText;
private final ChooserContentPreviewUi.ActionFactory mActionFactory;
private final ImageLoader mImageLoader;
private final MimeTypeClassifier mTypeClassifier;
@@ -53,14 +46,12 @@ class UnifiedContentPreviewUi extends ContentPreviewUi {
UnifiedContentPreviewUi(
List<FileInfo> files,
- @Nullable CharSequence text,
ChooserContentPreviewUi.ActionFactory actionFactory,
ImageLoader imageLoader,
MimeTypeClassifier typeClassifier,
TransitionElementStatusCallback transitionElementStatusCallback,
HeadlineGenerator headlineGenerator) {
mFiles = files;
- mText = text;
mActionFactory = actionFactory;
mImageLoader = imageLoader;
mTypeClassifier = typeClassifier;
@@ -131,20 +122,15 @@ class UnifiedContentPreviewUi extends ContentPreviewUi {
mFiles.size() - previews.size(),
mImageLoader);
- if (!TextUtils.isEmpty(mText) && mFiles.size() == 1 && allImages) {
- setTextInImagePreviewVisibility(contentPreviewLayout, imagePreview, mActionFactory);
- updateTextWithImageHeadline(contentPreviewLayout);
+ if (allImages) {
+ displayHeadline(
+ contentPreviewLayout, mHeadlineGenerator.getImagesHeadline(mFiles.size()));
+ } else if (allVideos) {
+ displayHeadline(
+ contentPreviewLayout, mHeadlineGenerator.getVideosHeadline(mFiles.size()));
} else {
- if (allImages) {
- displayHeadline(
- contentPreviewLayout, mHeadlineGenerator.getImagesHeadline(mFiles.size()));
- } else if (allVideos) {
- displayHeadline(
- contentPreviewLayout, mHeadlineGenerator.getVideosHeadline(mFiles.size()));
- } else {
- displayHeadline(
- contentPreviewLayout, mHeadlineGenerator.getItemsHeadline(mFiles.size()));
- }
+ displayHeadline(
+ contentPreviewLayout, mHeadlineGenerator.getItemsHeadline(mFiles.size()));
}
return contentPreviewLayout;
@@ -166,50 +152,6 @@ class UnifiedContentPreviewUi extends ContentPreviewUi {
return actions;
}
- private void updateTextWithImageHeadline(ViewGroup contentPreview) {
- CheckBox actionView = contentPreview.requireViewById(R.id.include_text_action);
- if (actionView.getVisibility() == View.VISIBLE && actionView.isChecked()) {
- displayHeadline(contentPreview, mHeadlineGenerator.getImageWithTextHeadline(mText));
- } else {
- displayHeadline(
- contentPreview, mHeadlineGenerator.getImagesHeadline(mFiles.size()));
- }
- }
-
- private void setTextInImagePreviewVisibility(
- ViewGroup contentPreview,
- ScrollableImagePreviewView imagePreview,
- ChooserContentPreviewUi.ActionFactory actionFactory) {
- final TextView textView = contentPreview
- .requireViewById(com.android.internal.R.id.content_preview_text);
- CheckBox actionView = contentPreview
- .requireViewById(R.id.include_text_action);
- textView.setVisibility(View.VISIBLE);
- boolean isLink = HttpUriMatcher.isHttpUri(mText.toString());
- textView.setAutoLinkMask(isLink ? Linkify.WEB_URLS : 0);
- textView.setText(mText);
-
- final int[] actionLabels = isLink
- ? new int[] { R.string.include_link, R.string.exclude_link }
- : new int[] { R.string.include_text, R.string.exclude_text };
- final Consumer<Boolean> shareTextAction = actionFactory.getExcludeSharedTextAction();
- actionView.setChecked(true);
- actionView.setText(actionLabels[1]);
- shareTextAction.accept(false);
- actionView.setOnCheckedChangeListener((view, isChecked) -> {
- view.setText(actionLabels[isChecked ? 1 : 0]);
- textView.setEnabled(isChecked);
- if (imagePreview.getVisibility() == View.VISIBLE) {
- // animate only only if we have preview
- TransitionManager.beginDelayedTransition((ViewGroup) textView.getParent());
- textView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
- }
- shareTextAction.accept(!isChecked);
- updateTextWithImageHeadline(contentPreview);
- });
- actionView.setVisibility(View.VISIBLE);
- }
-
private ScrollableImagePreviewView.PreviewType getPreviewType(String mimeType) {
if (mTypeClassifier.isImageType(mimeType)) {
return ScrollableImagePreviewView.PreviewType.Image;