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.util.UriFilters.isOwnedByCurrentUser; 20 21 import android.content.res.Resources; 22 import android.net.Uri; 23 import android.text.SpannableStringBuilder; 24 import android.text.TextUtils; 25 import android.util.Size; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ImageView; 30 import android.widget.TextView; 31 32 import androidx.annotation.Nullable; 33 import androidx.core.view.ViewCompat; 34 35 import com.android.intentresolver.ContentTypeHint; 36 import com.android.intentresolver.R; 37 import com.android.intentresolver.widget.ActionRow; 38 import com.android.intentresolver.widget.ViewRoleDescriptionAccessibilityDelegate; 39 40 import kotlinx.coroutines.CoroutineScope; 41 42 class TextContentPreviewUi extends ContentPreviewUi { 43 private final CoroutineScope mScope; 44 @Nullable 45 private final CharSequence mSharingText; 46 @Nullable 47 private final CharSequence mPreviewTitle; 48 @Nullable 49 private final CharSequence mMetadata; 50 @Nullable 51 private final Uri mPreviewThumbnail; 52 private final ImageLoader mImageLoader; 53 private final ChooserContentPreviewUi.ActionFactory mActionFactory; 54 private final HeadlineGenerator mHeadlineGenerator; 55 private final ContentTypeHint mContentTypeHint; 56 private int mPreviewSize; 57 TextContentPreviewUi( CoroutineScope scope, @Nullable CharSequence sharingText, @Nullable CharSequence previewTitle, @Nullable CharSequence metadata, @Nullable Uri previewThumbnail, ChooserContentPreviewUi.ActionFactory actionFactory, ImageLoader imageLoader, HeadlineGenerator headlineGenerator, ContentTypeHint contentTypeHint)58 TextContentPreviewUi( 59 CoroutineScope scope, 60 @Nullable CharSequence sharingText, 61 @Nullable CharSequence previewTitle, 62 @Nullable CharSequence metadata, 63 @Nullable Uri previewThumbnail, 64 ChooserContentPreviewUi.ActionFactory actionFactory, 65 ImageLoader imageLoader, 66 HeadlineGenerator headlineGenerator, 67 ContentTypeHint contentTypeHint) { 68 mScope = scope; 69 mSharingText = sharingText; 70 mPreviewTitle = previewTitle; 71 mMetadata = metadata; 72 mPreviewThumbnail = previewThumbnail; 73 mImageLoader = imageLoader; 74 mActionFactory = actionFactory; 75 mHeadlineGenerator = headlineGenerator; 76 mContentTypeHint = contentTypeHint; 77 } 78 79 @Override getType()80 public int getType() { 81 return ContentPreviewType.CONTENT_PREVIEW_TEXT; 82 } 83 84 @Override display( Resources resources, LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent)85 public ViewGroup display( 86 Resources resources, 87 LayoutInflater layoutInflater, 88 ViewGroup parent, 89 View headlineViewParent) { 90 mPreviewSize = resources.getDimensionPixelSize(R.dimen.width_text_image_preview_size); 91 return displayInternal(layoutInflater, parent, headlineViewParent); 92 } 93 displayInternal( LayoutInflater layoutInflater, ViewGroup parent, View headlineViewParent)94 private ViewGroup displayInternal( 95 LayoutInflater layoutInflater, 96 ViewGroup parent, 97 View headlineViewParent) { 98 ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( 99 R.layout.chooser_grid_preview_text, parent, false); 100 inflateHeadline(headlineViewParent); 101 102 final ActionRow actionRow = 103 contentPreviewLayout.findViewById(com.android.internal.R.id.chooser_action_row); 104 actionRow.setActions(mActionFactory.createCustomActions()); 105 106 if (mSharingText == null) { 107 contentPreviewLayout 108 .findViewById(R.id.text_preview_layout) 109 .setVisibility(View.GONE); 110 return contentPreviewLayout; 111 } 112 113 TextView textView = contentPreviewLayout.findViewById( 114 com.android.internal.R.id.content_preview_text); 115 116 textView.setText( 117 textView.getMaxLines() == 1 ? replaceLineBreaks(mSharingText) : mSharingText); 118 119 TextView previewTitleView = contentPreviewLayout.findViewById( 120 com.android.internal.R.id.content_preview_title); 121 if (TextUtils.isEmpty(mPreviewTitle)) { 122 previewTitleView.setVisibility(View.GONE); 123 } else { 124 previewTitleView.setText(mPreviewTitle); 125 } 126 127 final ImageView previewThumbnailView = contentPreviewLayout.requireViewById( 128 com.android.internal.R.id.content_preview_thumbnail); 129 if (!isOwnedByCurrentUser(mPreviewThumbnail)) { 130 previewThumbnailView.setVisibility(View.GONE); 131 } else { 132 mImageLoader.loadImage( 133 mScope, 134 mPreviewThumbnail, 135 new Size(mPreviewSize, mPreviewSize), 136 (bitmap) -> updateViewWithImage( 137 previewThumbnailView, 138 bitmap)); 139 } 140 141 Runnable onCopy = mActionFactory.getCopyButtonRunnable(); 142 View copyButton = contentPreviewLayout.findViewById(R.id.copy); 143 if (copyButton != null) { 144 if (onCopy != null) { 145 copyButton.setContentDescription( 146 mHeadlineGenerator.getCopyButtonContentDescription(mSharingText)); 147 copyButton.setOnClickListener((v) -> onCopy.run()); 148 ViewCompat.setAccessibilityDelegate( 149 copyButton, 150 new ViewRoleDescriptionAccessibilityDelegate( 151 layoutInflater.getContext() 152 .getString(R.string.role_description_button))); 153 } else { 154 copyButton.setVisibility(View.GONE); 155 } 156 } 157 158 String headlineText = (mContentTypeHint == ContentTypeHint.ALBUM) 159 ? mHeadlineGenerator.getAlbumHeadline() 160 : mHeadlineGenerator.getTextHeadline(mSharingText); 161 displayHeadline(headlineViewParent, headlineText); 162 displayMetadata(headlineViewParent, mMetadata); 163 164 return contentPreviewLayout; 165 } 166 167 @Nullable replaceLineBreaks(@ullable CharSequence text)168 private static CharSequence replaceLineBreaks(@Nullable CharSequence text) { 169 if (text == null) { 170 return null; 171 } 172 SpannableStringBuilder string = new SpannableStringBuilder(text); 173 for (int i = 0, size = string.length(); i < size; i++) { 174 if (string.charAt(i) == '\n') { 175 string.replace(i, i + 1, " "); 176 } 177 } 178 return string; 179 } 180 } 181