• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.intentresolver.contentpreview;
18 
19 import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE;
20 
21 import android.content.res.Resources;
22 import android.util.Log;
23 import android.util.Size;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 
28 import androidx.annotation.Nullable;
29 
30 import com.android.intentresolver.R;
31 import com.android.intentresolver.widget.ActionRow;
32 import com.android.intentresolver.widget.ImagePreviewView.TransitionElementStatusCallback;
33 import com.android.intentresolver.widget.ScrollableImagePreviewView;
34 
35 import kotlin.Pair;
36 
37 import kotlinx.coroutines.CoroutineScope;
38 import kotlinx.coroutines.flow.Flow;
39 
40 import java.util.List;
41 import java.util.Objects;
42 
43 class UnifiedContentPreviewUi extends ContentPreviewUi {
44     private final boolean mShowEditAction;
45     @Nullable
46     private final String mIntentMimeType;
47     private final ChooserContentPreviewUi.ActionFactory mActionFactory;
48     private final ImageLoader mImageLoader;
49     private final MimeTypeClassifier mTypeClassifier;
50     private final TransitionElementStatusCallback mTransitionElementStatusCallback;
51     private final HeadlineGenerator mHeadlineGenerator;
52     @Nullable
53     private final CharSequence mMetadata;
54     private final Flow<FileInfo> mFileInfoFlow;
55     private final int mItemCount;
56     @Nullable
57     private List<FileInfo> mFiles;
58     @Nullable
59     private ViewGroup mContentPreviewView;
60     private View mHeadlineView;
61     private int mPreviewSize;
62 
UnifiedContentPreviewUi( CoroutineScope scope, boolean isSingleImage, @Nullable String intentMimeType, ChooserContentPreviewUi.ActionFactory actionFactory, ImageLoader imageLoader, MimeTypeClassifier typeClassifier, TransitionElementStatusCallback transitionElementStatusCallback, Flow<FileInfo> fileInfoFlow, int itemCount, HeadlineGenerator headlineGenerator, @Nullable CharSequence metadata)63     UnifiedContentPreviewUi(
64             CoroutineScope scope,
65             boolean isSingleImage,
66             @Nullable String intentMimeType,
67             ChooserContentPreviewUi.ActionFactory actionFactory,
68             ImageLoader imageLoader,
69             MimeTypeClassifier typeClassifier,
70             TransitionElementStatusCallback transitionElementStatusCallback,
71             Flow<FileInfo> fileInfoFlow,
72             int itemCount,
73             HeadlineGenerator headlineGenerator,
74             @Nullable CharSequence metadata) {
75         mShowEditAction = isSingleImage;
76         mIntentMimeType = intentMimeType;
77         mActionFactory = actionFactory;
78         mImageLoader = imageLoader;
79         mTypeClassifier = typeClassifier;
80         mTransitionElementStatusCallback = transitionElementStatusCallback;
81         mFileInfoFlow = fileInfoFlow;
82         mItemCount = itemCount;
83         mHeadlineGenerator = headlineGenerator;
84         mMetadata = metadata;
85 
86         JavaFlowHelper.collectToList(scope, fileInfoFlow, this::setFiles);
87     }
88 
89     @Override
getType()90     public int getType() {
91         return CONTENT_PREVIEW_IMAGE;
92     }
93 
94     @Override
display( Resources resources, LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent)95     public ViewGroup display(
96             Resources resources,
97             LayoutInflater layoutInflater,
98             ViewGroup parent,
99             View headlineViewParent) {
100         mPreviewSize = resources.getDimensionPixelSize(R.dimen.chooser_preview_image_max_dimen);
101         return displayInternal(layoutInflater, parent, headlineViewParent);
102     }
103 
setFiles(List<FileInfo> files)104     private void setFiles(List<FileInfo> files) {
105         Size previewSize = new Size(mPreviewSize, mPreviewSize);
106         mImageLoader.prePopulate(
107                 files.stream()
108                         .map(FileInfo::getPreviewUri)
109                         .filter(Objects::nonNull)
110                         .map((uri -> new Pair<>(uri, previewSize)))
111                         .toList());
112         mFiles = files;
113         if (mContentPreviewView != null) {
114             updatePreviewWithFiles(mContentPreviewView, mHeadlineView, files);
115         }
116     }
117 
displayInternal( LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent)118     private ViewGroup displayInternal(
119             LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent) {
120         mContentPreviewView = (ViewGroup) layoutInflater.inflate(
121                 R.layout.chooser_grid_preview_image, parent, false);
122         mHeadlineView = headlineViewParent;
123         inflateHeadline(mHeadlineView);
124 
125         final ActionRow actionRow =
126                 mContentPreviewView.findViewById(com.android.internal.R.id.chooser_action_row);
127         List<ActionRow.Action> actions = mActionFactory.createCustomActions();
128         actionRow.setActions(actions);
129 
130         ScrollableImagePreviewView imagePreview =
131                 mContentPreviewView.requireViewById(R.id.scrollable_image_preview);
132         imagePreview.setPreviewHeight(mPreviewSize);
133         imagePreview.setImageLoader(mImageLoader);
134         imagePreview.setOnNoPreviewCallback(() -> imagePreview.setVisibility(View.GONE));
135         imagePreview.setTransitionElementStatusCallback(mTransitionElementStatusCallback);
136         imagePreview.setPreviews(
137                 JavaFlowHelper.mapFileIntoToPreview(
138                         mFileInfoFlow,
139                         mTypeClassifier,
140                         mShowEditAction ? mActionFactory.getEditButtonRunnable() : null),
141                 mItemCount);
142 
143         if (mFiles != null) {
144             updatePreviewWithFiles(mContentPreviewView, mHeadlineView, mFiles);
145         } else {
146             displayHeadline(
147                     mHeadlineView,
148                     mItemCount,
149                     mTypeClassifier.isImageType(mIntentMimeType),
150                     mTypeClassifier.isVideoType(mIntentMimeType));
151             imagePreview.setLoading(mItemCount);
152         }
153 
154         return mContentPreviewView;
155     }
156 
updatePreviewWithFiles( ViewGroup contentPreviewView, View headlineView, List<FileInfo> files)157     private void updatePreviewWithFiles(
158             ViewGroup contentPreviewView, View headlineView, List<FileInfo> files) {
159         final int count = files.size();
160         ScrollableImagePreviewView imagePreview =
161                 contentPreviewView.requireViewById(R.id.scrollable_image_preview);
162         if (count == 0) {
163             Log.i(
164                     TAG,
165                     "Attempted to display image preview area with zero"
166                             + " available images detected in EXTRA_STREAM list");
167             imagePreview.setVisibility(View.GONE);
168             mTransitionElementStatusCallback.onAllTransitionElementsReady();
169             return;
170         }
171 
172         boolean allImages = true;
173         boolean allVideos = true;
174         for (FileInfo fileInfo : files) {
175             ScrollableImagePreviewView.PreviewType previewType =
176                     getPreviewType(mTypeClassifier, fileInfo.getMimeType());
177             allImages = allImages && previewType == ScrollableImagePreviewView.PreviewType.Image;
178             allVideos = allVideos && previewType == ScrollableImagePreviewView.PreviewType.Video;
179         }
180 
181         displayHeadline(headlineView, count, allImages, allVideos);
182     }
183 
displayHeadline( View layout, int count, boolean allImages, boolean allVideos)184     private void displayHeadline(
185             View layout, int count, boolean allImages, boolean allVideos) {
186         if (allImages) {
187             displayHeadline(layout, mHeadlineGenerator.getImagesHeadline(count));
188         } else if (allVideos) {
189             displayHeadline(layout, mHeadlineGenerator.getVideosHeadline(count));
190         } else {
191             displayHeadline(layout, mHeadlineGenerator.getFilesHeadline(count));
192         }
193         displayMetadata(layout, mMetadata);
194     }
195 }
196