/* * 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_IMAGE; import android.content.res.Resources; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; import com.android.intentresolver.R; import com.android.intentresolver.widget.ActionRow; import com.android.intentresolver.widget.ImagePreviewView.TransitionElementStatusCallback; import com.android.intentresolver.widget.ScrollableImagePreviewView; import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.flow.Flow; import java.util.List; import java.util.Objects; class UnifiedContentPreviewUi extends ContentPreviewUi { private final boolean mShowEditAction; @Nullable private final String mIntentMimeType; private final ChooserContentPreviewUi.ActionFactory mActionFactory; private final ImageLoader mImageLoader; private final MimeTypeClassifier mTypeClassifier; private final TransitionElementStatusCallback mTransitionElementStatusCallback; private final HeadlineGenerator mHeadlineGenerator; @Nullable private final CharSequence mMetadata; private final Flow mFileInfoFlow; private final int mItemCount; @Nullable private List mFiles; @Nullable private ViewGroup mContentPreviewView; private View mHeadlineView; UnifiedContentPreviewUi( CoroutineScope scope, boolean isSingleImage, @Nullable String intentMimeType, ChooserContentPreviewUi.ActionFactory actionFactory, ImageLoader imageLoader, MimeTypeClassifier typeClassifier, TransitionElementStatusCallback transitionElementStatusCallback, Flow fileInfoFlow, int itemCount, HeadlineGenerator headlineGenerator, @Nullable CharSequence metadata) { mShowEditAction = isSingleImage; mIntentMimeType = intentMimeType; mActionFactory = actionFactory; mImageLoader = imageLoader; mTypeClassifier = typeClassifier; mTransitionElementStatusCallback = transitionElementStatusCallback; mFileInfoFlow = fileInfoFlow; mItemCount = itemCount; mHeadlineGenerator = headlineGenerator; mMetadata = metadata; JavaFlowHelper.collectToList(scope, fileInfoFlow, this::setFiles); } @Override public int getType() { return CONTENT_PREVIEW_IMAGE; } @Override public ViewGroup display( Resources resources, LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent) { return displayInternal(layoutInflater, parent, headlineViewParent); } private void setFiles(List files) { mImageLoader.prePopulate(files.stream() .map(FileInfo::getPreviewUri) .filter(Objects::nonNull) .toList()); mFiles = files; if (mContentPreviewView != null) { updatePreviewWithFiles(mContentPreviewView, mHeadlineView, files); } } private ViewGroup displayInternal( LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent) { mContentPreviewView = (ViewGroup) layoutInflater.inflate( R.layout.chooser_grid_preview_image, parent, false); mHeadlineView = headlineViewParent; inflateHeadline(mHeadlineView); final ActionRow actionRow = mContentPreviewView.findViewById(com.android.internal.R.id.chooser_action_row); List actions = mActionFactory.createCustomActions(); actionRow.setActions(actions); ScrollableImagePreviewView imagePreview = mContentPreviewView.requireViewById(R.id.scrollable_image_preview); imagePreview.setImageLoader(mImageLoader); imagePreview.setOnNoPreviewCallback(() -> imagePreview.setVisibility(View.GONE)); imagePreview.setTransitionElementStatusCallback(mTransitionElementStatusCallback); imagePreview.setPreviews( JavaFlowHelper.mapFileIntoToPreview( mFileInfoFlow, mTypeClassifier, mShowEditAction ? mActionFactory.getEditButtonRunnable() : null), mItemCount); if (mFiles != null) { updatePreviewWithFiles(mContentPreviewView, mHeadlineView, mFiles); } else { displayHeadline( mHeadlineView, mItemCount, mTypeClassifier.isImageType(mIntentMimeType), mTypeClassifier.isVideoType(mIntentMimeType)); imagePreview.setLoading(mItemCount); } return mContentPreviewView; } private void updatePreviewWithFiles( ViewGroup contentPreviewView, View headlineView, List files) { final int count = files.size(); ScrollableImagePreviewView imagePreview = contentPreviewView.requireViewById(R.id.scrollable_image_preview); if (count == 0) { Log.i( TAG, "Attempted to display image preview area with zero" + " available images detected in EXTRA_STREAM list"); imagePreview.setVisibility(View.GONE); mTransitionElementStatusCallback.onAllTransitionElementsReady(); return; } boolean allImages = true; boolean allVideos = true; for (FileInfo fileInfo : files) { ScrollableImagePreviewView.PreviewType previewType = getPreviewType(mTypeClassifier, fileInfo.getMimeType()); allImages = allImages && previewType == ScrollableImagePreviewView.PreviewType.Image; allVideos = allVideos && previewType == ScrollableImagePreviewView.PreviewType.Video; } displayHeadline(headlineView, count, allImages, allVideos); } private void displayHeadline( View layout, int count, boolean allImages, boolean allVideos) { if (allImages) { displayHeadline(layout, mHeadlineGenerator.getImagesHeadline(count)); } else if (allVideos) { displayHeadline(layout, mHeadlineGenerator.getVideosHeadline(count)); } else { displayHeadline(layout, mHeadlineGenerator.getFilesHeadline(count)); } displayMetadata(layout, mMetadata); } }