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