1 /* 2 * Copyright (C) 2015 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.base.DocumentInfo.getCursorInt; 20 import static com.android.documentsui.base.DocumentInfo.getCursorString; 21 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.graphics.Rect; 25 import android.text.format.Formatter; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ImageView; 30 import android.widget.LinearLayout; 31 import android.widget.TextView; 32 33 import androidx.annotation.Nullable; 34 35 import com.android.documentsui.R; 36 import com.android.documentsui.base.DocumentInfo; 37 import com.android.documentsui.base.Lookup; 38 import com.android.documentsui.base.Shared; 39 import com.android.documentsui.base.State; 40 import com.android.documentsui.base.UserId; 41 import com.android.documentsui.roots.RootCursorWrapper; 42 import com.android.documentsui.ui.Views; 43 44 import java.util.function.Function; 45 46 final class ListDocumentHolder extends DocumentHolder { 47 48 private final TextView mTitle; 49 private final @Nullable LinearLayout mDetails; // Container of date/size/summary 50 private final TextView mDate; 51 private final TextView mSize; 52 private final TextView mType; 53 private final ImageView mIconMime; 54 private final ImageView mIconThumb; 55 private final ImageView mIconCheck; 56 private final ImageView mIconBriefcase; 57 private final View mIconLayout; 58 final View mPreviewIcon; 59 60 private final IconHelper mIconHelper; 61 private final Lookup<String, String> mFileTypeLookup; 62 // This is used in as a convenience in our bind method. 63 private final DocumentInfo mDoc; 64 ListDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper, Lookup<String, String> fileTypeLookup)65 public ListDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper, 66 Lookup<String, String> fileTypeLookup) { 67 super(context, parent, R.layout.item_doc_list); 68 69 mIconLayout = itemView.findViewById(R.id.icon); 70 mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime); 71 mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb); 72 mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check); 73 mIconBriefcase = (ImageView) itemView.findViewById(R.id.icon_briefcase); 74 mTitle = (TextView) itemView.findViewById(android.R.id.title); 75 mSize = (TextView) itemView.findViewById(R.id.size); 76 mDate = (TextView) itemView.findViewById(R.id.date); 77 mType = (TextView) itemView.findViewById(R.id.file_type); 78 // Warning: mDetails view doesn't exists in layout-sw720dp-land layout 79 mDetails = (LinearLayout) itemView.findViewById(R.id.line2); 80 mPreviewIcon = itemView.findViewById(R.id.preview_icon); 81 82 mIconHelper = iconHelper; 83 mFileTypeLookup = fileTypeLookup; 84 mDoc = new DocumentInfo(); 85 } 86 87 @Override setSelected(boolean selected, boolean animate)88 public void setSelected(boolean selected, boolean animate) { 89 // We always want to make sure our check box disappears if we're not selected, 90 // even if the item is disabled. But it should be an error (see assert below) 91 // to be set to selected && be disabled. 92 float checkAlpha = selected ? 1f : 0f; 93 if (animate) { 94 fade(mIconCheck, checkAlpha).start(); 95 } else { 96 mIconCheck.setAlpha(checkAlpha); 97 } 98 99 if (!itemView.isEnabled()) { 100 assert(!selected); 101 return; 102 } 103 104 super.setSelected(selected, animate); 105 106 if (animate) { 107 fade(mIconMime, 1f - checkAlpha).start(); 108 fade(mIconThumb, 1f - checkAlpha).start(); 109 } else { 110 mIconMime.setAlpha(1f - checkAlpha); 111 mIconThumb.setAlpha(1f - checkAlpha); 112 } 113 } 114 115 @Override setEnabled(boolean enabled)116 public void setEnabled(boolean enabled) { 117 super.setEnabled(enabled); 118 119 // Text colors enabled/disabled is handle via a color set. 120 final float imgAlpha = enabled ? 1f : DISABLED_ALPHA; 121 mIconMime.setAlpha(imgAlpha); 122 mIconThumb.setAlpha(imgAlpha); 123 } 124 125 @Override bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback)126 public void bindPreviewIcon(boolean show, Function<View, Boolean> clickCallback) { 127 if (mDoc.isDirectory()) { 128 mPreviewIcon.setVisibility(View.GONE); 129 } else { 130 mPreviewIcon.setVisibility(show ? View.VISIBLE : View.GONE); 131 if (show) { 132 mPreviewIcon.setContentDescription( 133 itemView.getResources().getString( 134 mIconHelper.shouldShowBadge(mDoc.userId.getIdentifier()) 135 ? R.string.preview_work_file 136 : R.string.preview_file, mDoc.displayName)); 137 mPreviewIcon.setAccessibilityDelegate( 138 new PreviewAccessibilityDelegate(clickCallback)); 139 } 140 } 141 } 142 143 @Override bindBriefcaseIcon(boolean show)144 public void bindBriefcaseIcon(boolean show) { 145 mIconBriefcase.setVisibility(show ? View.VISIBLE : View.GONE); 146 } 147 148 @Override inDragRegion(MotionEvent event)149 public boolean inDragRegion(MotionEvent event) { 150 // If itemView is activated = selected, then whole region is interactive 151 if (itemView.isActivated()) { 152 return true; 153 } 154 155 // Do everything in global coordinates - it makes things simpler. 156 int[] coords = new int[2]; 157 mIconLayout.getLocationOnScreen(coords); 158 159 Rect textBounds = new Rect(); 160 mTitle.getPaint().getTextBounds( 161 mTitle.getText().toString(), 0, mTitle.getText().length(), textBounds); 162 163 Rect rect = new Rect( 164 coords[0], 165 coords[1], 166 coords[0] + mIconLayout.getWidth() + textBounds.width(), 167 coords[1] + Math.max(mIconLayout.getHeight(), textBounds.height())); 168 169 // If the tap occurred inside icon or the text, these are interactive spots. 170 return rect.contains((int) event.getRawX(), (int) event.getRawY()); 171 } 172 173 @Override inSelectRegion(MotionEvent event)174 public boolean inSelectRegion(MotionEvent event) { 175 return (mDoc.isDirectory() && !(mAction == State.ACTION_BROWSE)) ? 176 false : Views.isEventOver(event, mIconLayout); 177 } 178 179 @Override inPreviewIconRegion(MotionEvent event)180 public boolean inPreviewIconRegion(MotionEvent event) { 181 return Views.isEventOver(event, mPreviewIcon); 182 } 183 184 /** 185 * Bind this view to the given document for display. 186 * @param cursor Pointing to the item to be bound. 187 * @param modelId The model ID of the item. 188 */ 189 @Override bind(Cursor cursor, String modelId)190 public void bind(Cursor cursor, String modelId) { 191 assert(cursor != null); 192 193 mModelId = modelId; 194 195 mDoc.updateFromCursor(cursor, 196 UserId.of(getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID)), 197 getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY)); 198 199 mIconHelper.stopLoading(mIconThumb); 200 201 mIconMime.animate().cancel(); 202 mIconMime.setAlpha(1f); 203 mIconThumb.animate().cancel(); 204 mIconThumb.setAlpha(0f); 205 206 mIconHelper.load(mDoc, mIconThumb, mIconMime, null); 207 208 mTitle.setText(mDoc.displayName, TextView.BufferType.SPANNABLE); 209 mTitle.setVisibility(View.VISIBLE); 210 211 212 boolean hasDetails = false; 213 if (mDoc.isDirectory()) { 214 // Note, we don't show any details for any directory...ever. 215 hasDetails = false; 216 } else { 217 if (mDoc.lastModified > 0) { 218 hasDetails = true; 219 mDate.setText(Shared.formatTime(mContext, mDoc.lastModified)); 220 } else { 221 mDate.setText(null); 222 } 223 224 if (mDoc.size > -1) { 225 hasDetails = true; 226 mSize.setVisibility(View.VISIBLE); 227 mSize.setText(Formatter.formatFileSize(mContext, mDoc.size)); 228 } else { 229 mSize.setVisibility(View.INVISIBLE); 230 } 231 232 mType.setText(mFileTypeLookup.lookup(mDoc.mimeType)); 233 } 234 235 // mDetails view doesn't exists in layout-sw720dp-land layout 236 if (mDetails != null) { 237 mDetails.setVisibility(hasDetails ? View.VISIBLE : View.GONE); 238 } 239 240 // TODO: Add document debug info 241 // Call includeDebugInfo 242 } 243 } 244