1 /* 2 * Copyright (C) 2018 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.documentsui.dirlist; 18 19 import static com.android.documentsui.DevicePolicyResources.Drawables.Style.SOLID_NOT_COLORED; 20 import static com.android.documentsui.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; 21 import static com.android.documentsui.base.DocumentInfo.getCursorInt; 22 import static com.android.documentsui.base.DocumentInfo.getCursorLong; 23 import static com.android.documentsui.base.DocumentInfo.getCursorString; 24 25 import android.app.admin.DevicePolicyManager; 26 import android.content.Context; 27 import android.database.Cursor; 28 import android.graphics.drawable.Drawable; 29 import android.os.Build; 30 import android.provider.DocumentsContract.Document; 31 import android.text.format.Formatter; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.widget.ImageView; 36 37 import androidx.annotation.RequiresApi; 38 39 import com.android.documentsui.ConfigStore; 40 import com.android.documentsui.DocumentsApplication; 41 import com.android.documentsui.R; 42 import com.android.documentsui.base.DocumentInfo; 43 import com.android.documentsui.base.Shared; 44 import com.android.documentsui.base.UserId; 45 import com.android.documentsui.roots.RootCursorWrapper; 46 import com.android.documentsui.ui.Views; 47 import com.android.modules.utils.build.SdkLevel; 48 49 import java.util.Map; 50 import java.util.function.Function; 51 52 final class GridPhotoHolder extends DocumentHolder { 53 54 private final ImageView mIconMimeLg; 55 private final ImageView mIconThumb; 56 private final ImageView mIconCheck; 57 private final IconHelper mIconHelper; 58 private final View mPreviewIcon; 59 private final View mIconBadge; 60 61 // This is used in as a convenience in our bind method. 62 private final DocumentInfo mDoc = new DocumentInfo(); 63 GridPhotoHolder(Context context, ViewGroup parent, IconHelper iconHelper, ConfigStore configStore)64 GridPhotoHolder(Context context, ViewGroup parent, IconHelper iconHelper, 65 ConfigStore configStore) { 66 super(context, parent, R.layout.item_photo_grid, configStore); 67 68 mIconMimeLg = (ImageView) itemView.findViewById(R.id.icon_mime_lg); 69 mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb); 70 mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check); 71 mIconBadge = itemView.findViewById(R.id.icon_profile_badge); 72 mPreviewIcon = itemView.findViewById(R.id.preview_icon); 73 74 mIconHelper = iconHelper; 75 76 if (SdkLevel.isAtLeastT() && !mConfigStore.isPrivateSpaceInDocsUIEnabled()) { 77 setUpdatableWorkProfileIcon(context); 78 } 79 } 80 81 @RequiresApi(Build.VERSION_CODES.TIRAMISU) setUpdatableWorkProfileIcon(Context context)82 private void setUpdatableWorkProfileIcon(Context context) { 83 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 84 Drawable drawable = dpm.getResources().getDrawable( 85 WORK_PROFILE_ICON, SOLID_NOT_COLORED, () -> 86 context.getDrawable(R.drawable.ic_briefcase)); 87 ImageView icon = (ImageView) mIconBadge.findViewById(R.id.icon_id); 88 89 icon.setImageDrawable(drawable); 90 } 91 92 @Override setSelected(boolean selected, boolean animate)93 public void setSelected(boolean selected, boolean animate) { 94 // We always want to make sure our check box disappears if we're not selected, 95 // even if the item is disabled. This is because this object can be reused 96 // and this method will be called to setup initial state. 97 float checkAlpha = selected ? 1f : 0f; 98 if (animate) { 99 fade(mIconCheck, checkAlpha).start(); 100 } else { 101 mIconCheck.setAlpha(checkAlpha); 102 } 103 104 // But it should be an error to be set to selected && be disabled. 105 if (!itemView.isEnabled()) { 106 assert (!selected); 107 return; 108 } 109 110 super.setSelected(selected, animate); 111 } 112 113 @Override setEnabled(boolean enabled)114 public void setEnabled(boolean enabled) { 115 super.setEnabled(enabled); 116 117 float imgAlpha = enabled ? 1f : DISABLED_ALPHA; 118 119 mIconMimeLg.setAlpha(imgAlpha); 120 mIconThumb.setAlpha(imgAlpha); 121 } 122 123 @Override bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback)124 public void bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback) { 125 mPreviewIcon.setVisibility(show ? View.VISIBLE : View.GONE); 126 if (show) { 127 mPreviewIcon.setContentDescription( 128 getPreviewIconContentDescription( 129 mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier()), 130 mDoc.displayName, mDoc.userId)); 131 mPreviewIcon.setAccessibilityDelegate(new PreviewAccessibilityDelegate(clickCallback)); 132 } 133 } 134 135 @Override bindBriefcaseIcon(boolean show)136 public void bindBriefcaseIcon(boolean show) { 137 mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE); 138 } 139 140 @Override 141 @RequiresApi(Build.VERSION_CODES.S) bindProfileIcon(boolean show, int userIdIdentifier)142 public void bindProfileIcon(boolean show, int userIdIdentifier) { 143 Map<UserId, Drawable> userIdToBadgeMap = DocumentsApplication.getUserManagerState( 144 mContext).getUserIdToBadgeMap(); 145 Drawable drawable = userIdToBadgeMap.get(UserId.of(userIdIdentifier)); 146 ImageView icon = mIconBadge.findViewById(R.id.icon_id); 147 icon.setImageDrawable(drawable); 148 mIconBadge.setVisibility(show ? View.VISIBLE : View.GONE); 149 mIconBadge.setContentDescription(mIconHelper.getProfileLabel(userIdIdentifier)); 150 } 151 152 @Override inDragRegion(MotionEvent event)153 public boolean inDragRegion(MotionEvent event) { 154 // Entire grid box should be draggable 155 return true; 156 } 157 158 @Override inSelectRegion(MotionEvent event)159 public boolean inSelectRegion(MotionEvent event) { 160 // Photo gird should not have any select region. 161 return false; 162 } 163 164 @Override inPreviewIconRegion(MotionEvent event)165 public boolean inPreviewIconRegion(MotionEvent event) { 166 return Views.isEventOver(event, itemView.getParent(), mPreviewIcon); 167 } 168 169 /** 170 * Bind this view to the given document for display. 171 * 172 * @param cursor Pointing to the item to be bound. 173 * @param modelId The model ID of the item. 174 */ 175 @Override bind(Cursor cursor, String modelId)176 public void bind(Cursor cursor, String modelId) { 177 assert (cursor != null); 178 179 mModelId = modelId; 180 181 mDoc.updateFromCursor(cursor, 182 UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)), 183 getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); 184 185 mIconHelper.stopLoading(mIconThumb); 186 187 mIconMimeLg.animate().cancel(); 188 mIconMimeLg.setAlpha(1f); 189 mIconThumb.animate().cancel(); 190 mIconThumb.setAlpha(0f); 191 192 mIconHelper.load(mDoc, mIconThumb, mIconMimeLg, null); 193 194 final String docSize = 195 Formatter.formatFileSize(mContext, getCursorLong(cursor, Document.COLUMN_SIZE)); 196 final String docDate = Shared.formatTime(mContext, mDoc.lastModified); 197 if (mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier())) { 198 itemView.setContentDescription( 199 mIconHelper.getProfileLabel(mDoc.userId.getIdentifier()) 200 + ", " 201 + mDoc.displayName 202 + ", " 203 + docSize 204 + ", " 205 + docDate); 206 } else { 207 itemView.setContentDescription(mDoc.displayName + ", " + docSize + ", " + docDate); 208 } 209 } 210 } 211