3feb370c2766974d757d337aaf759765553f7dec
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / adapter / FileListListAdapter.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 * Copyright (C) 2012-2014 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18 package com.owncloud.android.ui.adapter;
19
20
21 import java.io.File;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.Vector;
25
26 import third_parties.daveKoeller.AlphanumComparator;
27 import android.accounts.Account;
28 import android.content.Context;
29 import android.content.SharedPreferences;
30 import android.graphics.Bitmap;
31 import android.graphics.BitmapFactory;
32 import android.media.ThumbnailUtils;
33 import android.preference.PreferenceManager;
34 import android.text.format.DateUtils;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.BaseAdapter;
39 import android.widget.Filter;
40 import android.widget.Filterable;
41 import android.widget.GridView;
42 import android.widget.ImageView;
43 import android.widget.ListAdapter;
44 import android.widget.TextView;
45
46 import com.owncloud.android.R;
47 import com.owncloud.android.authentication.AccountUtils;
48 import com.owncloud.android.datamodel.FileDataStorageManager;
49 import com.owncloud.android.datamodel.OCFile;
50 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
51 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
52 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
53 import com.owncloud.android.ui.activity.ComponentsGetter;
54 import com.owncloud.android.utils.DisplayUtils;
55 import com.owncloud.android.utils.FileStorageUtils;
56
57
58 /**
59 * This Adapter populates a ListView with all files and folders in an ownCloud
60 * instance.
61 *
62 * @author Bartek Przybylski
63 * @author Tobias Kaminsky
64 * @author David A. Velasco
65 */
66 public class FileListListAdapter extends BaseAdapter implements ListAdapter {
67 private final static String PERMISSION_SHARED_WITH_ME = "S";
68
69 private Context mContext;
70 private OCFile mFile = null;
71 private Vector<OCFile> mFiles = null;
72 private Vector<OCFile> mFilesOrig = new Vector<OCFile>();
73 private boolean mJustFolders;
74
75 private FileDataStorageManager mStorageManager;
76 private Account mAccount;
77 private ComponentsGetter mTransferServiceGetter;
78 private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM };
79 private Integer mSortOrder;
80 public static final Integer SORT_NAME = 0;
81 public static final Integer SORT_DATE = 1;
82 public static final Integer SORT_SIZE = 2;
83 private Boolean mSortAscending;
84 private SharedPreferences mAppPreferences;
85
86 public FileListListAdapter(
87 boolean justFolders,
88 Context context,
89 ComponentsGetter transferServiceGetter
90 ) {
91
92 mJustFolders = justFolders;
93 mContext = context;
94 mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
95 mTransferServiceGetter = transferServiceGetter;
96
97 mAppPreferences = PreferenceManager
98 .getDefaultSharedPreferences(mContext);
99
100 // Read sorting order, default to sort by name ascending
101 FileStorageUtils.mSortOrder = mAppPreferences.getInt("sortOrder", 0);
102 FileStorageUtils.mSortAscending = mAppPreferences.getBoolean("sortAscending", true);
103
104
105 // initialise thumbnails cache on background thread
106 new ThumbnailsCacheManager.InitDiskCacheTask().execute();
107 }
108
109 @Override
110 public boolean areAllItemsEnabled() {
111 return true;
112 }
113
114 @Override
115 public boolean isEnabled(int position) {
116 return true;
117 }
118
119 @Override
120 public int getCount() {
121 return mFiles != null ? mFiles.size() : 0;
122 }
123
124 @Override
125 public Object getItem(int position) {
126 if (mFiles == null || mFiles.size() <= position)
127 return null;
128 return mFiles.get(position);
129 }
130
131 @Override
132 public long getItemId(int position) {
133 if (mFiles == null || mFiles.size() <= position)
134 return 0;
135 return mFiles.get(position).getFileId();
136 }
137
138 @Override
139 public int getItemViewType(int position) {
140 return 0;
141 }
142
143 @Override
144 public View getView(int position, View convertView, ViewGroup parent) {
145
146 boolean fileView = DisplayUtils.decideViewLayout(mFiles);
147
148 View view = convertView;
149 OCFile file = null;
150 LayoutInflater inflator = (LayoutInflater) mContext
151 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
152
153 if (mFiles != null && mFiles.size() > position) {
154 file = mFiles.get(position);
155 }
156
157 // Find out which layout should be displayed
158 ViewType viewType;
159 if (!fileView){
160 viewType = ViewType.LIST_ITEM;
161 } else if (file.isImage()){
162 viewType = ViewType.GRID_IMAGE;
163 } else {
164 viewType = ViewType.GRID_ITEM;
165 }
166
167 // Create View
168 switch (viewType){
169 case GRID_IMAGE:
170 view = inflator.inflate(R.layout.grid_image, null);
171 break;
172 case GRID_ITEM:
173 view = inflator.inflate(R.layout.grid_item, null);
174 break;
175 case LIST_ITEM:
176 view = inflator.inflate(R.layout.list_item, null);
177 break;
178 }
179
180 view.invalidate();
181
182 if (file != null){
183
184 ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
185 TextView fileName;
186 String name;
187
188 switch (viewType){
189 case LIST_ITEM:
190 fileName = (TextView) view.findViewById(R.id.Filename);
191 name = file.getFileName();
192 fileName.setText(name);
193
194 TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
195 TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
196 ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
197
198 lastModV.setVisibility(View.VISIBLE);
199 lastModV.setText(showRelativeTimestamp(file));
200
201 checkBoxV.setVisibility(View.GONE);
202
203 fileSizeV.setVisibility(View.VISIBLE);
204 fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
205
206 ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);
207
208
209 if (file.isShareByLink()) {
210 sharedIconV.setVisibility(View.VISIBLE);
211 } else {
212 sharedIconV.setVisibility(View.GONE);
213 }
214
215 ImageView localStateView = (ImageView) view.findViewById(R.id.localFileIndicator);
216
217 if (!file.isFolder()) {
218 GridView parentList = (GridView)parent;
219 if (parentList.getChoiceMode() == GridView.CHOICE_MODE_NONE) {
220 checkBoxV.setVisibility(View.GONE);
221 } else {
222 if (parentList.isItemChecked(position)) {
223 checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
224 } else {
225 checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
226 }
227 checkBoxV.setVisibility(View.VISIBLE);
228 }
229
230 localStateView.bringToFront();
231 FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
232 FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
233 if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
234 localStateView.setImageResource(R.drawable.downloading_file_indicator);
235 localStateView.setVisibility(View.VISIBLE);
236 } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
237 localStateView.setImageResource(R.drawable.uploading_file_indicator);
238 localStateView.setVisibility(View.VISIBLE);
239 } else if (file.isDown()) {
240 localStateView.setImageResource(R.drawable.local_file_indicator);
241 localStateView.setVisibility(View.VISIBLE);
242 } else {
243 localStateView.setVisibility(View.INVISIBLE);
244 }
245
246 ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);
247 if (checkIfFileIsSharedWithMe(file)) {
248 sharedWithMeIconV.setVisibility(View.VISIBLE);
249 } else {
250 sharedWithMeIconV.setVisibility(View.GONE);
251 }
252 } else {
253 localStateView.setVisibility(View.INVISIBLE);
254 }
255 break;
256 case GRID_ITEM:
257 fileName = (TextView) view.findViewById(R.id.Filename);
258 name = file.getFileName();
259 fileName.setText(name);
260 break;
261 case GRID_IMAGE:
262 break;
263 }
264
265 // For all Views
266
267 // this if-else is needed even though favorite icon is visible by default
268 // because android reuses views in listview
269 if (!file.keepInSync()) {
270 view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);
271 } else {
272 view.findViewById(R.id.favoriteIcon).setVisibility(View.VISIBLE);
273 }
274
275 // No Folder
276 if (!file.isFolder()) {
277 if (file.isImage() && file.getRemoteId() != null){
278 // Thumbnail in Cache?
279 Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
280 String.valueOf(file.getRemoteId())
281 );
282 if (thumbnail != null && !file.needsUpdateThumbnail()){
283 fileIcon.setImageBitmap(thumbnail);
284 } else {
285 // generate new Thumbnail
286 if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {
287 final ThumbnailsCacheManager.ThumbnailGenerationTask task =
288 new ThumbnailsCacheManager.ThumbnailGenerationTask(
289 fileIcon, mStorageManager, mAccount
290 );
291 if (thumbnail == null) {
292 thumbnail = ThumbnailsCacheManager.mDefaultImg;
293 }
294 final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
295 new ThumbnailsCacheManager.AsyncDrawable(
296 mContext.getResources(),
297 thumbnail,
298 task
299 );
300 fileIcon.setImageDrawable(asyncDrawable);
301 task.execute(file);
302 }
303 }
304 } else {
305 fileIcon.setImageResource(DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName()));
306 }
307 } else {
308 // Folder
309 if (checkIfFileIsSharedWithMe(file)) {
310 fileIcon.setImageResource(R.drawable.shared_with_me_folder);
311 } else if (file.isShareByLink()) {
312 // If folder is sharedByLink, icon folder must be changed to
313 // folder-public one
314 fileIcon.setImageResource(R.drawable.folder_public);
315 } else {
316 fileIcon.setImageResource(
317 DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName())
318 );
319 }
320 }
321 }
322
323 return view;
324 }
325
326 /**
327 * Local Folder size in human readable format
328 *
329 * @param path
330 * String
331 * @return Size in human readable format
332 */
333 private String getFolderSizeHuman(String path) {
334
335 File dir = new File(path);
336
337 if (dir.exists()) {
338 long bytes = FileStorageUtils.getFolderSize(dir);
339 return DisplayUtils.bytesToHumanReadable(bytes);
340 }
341
342 return "0 B";
343 }
344
345 /**
346 * Local Folder size
347 * @param dir File
348 * @return Size in bytes
349 */
350 private long getFolderSize(File dir) {
351 if (dir.exists()) {
352 long result = 0;
353 File[] fileList = dir.listFiles();
354 for(int i = 0; i < fileList.length; i++) {
355 if(fileList[i].isDirectory()) {
356 result += getFolderSize(fileList[i]);
357 } else {
358 result += fileList[i].length();
359 }
360 }
361 return result;
362 }
363 return 0;
364 }
365
366 @Override
367 public int getViewTypeCount() {
368 return 1;
369 }
370
371 @Override
372 public boolean hasStableIds() {
373 return true;
374 }
375
376 @Override
377 public boolean isEmpty() {
378 return (mFiles == null || mFiles.isEmpty());
379 }
380
381 /**
382 * Change the adapted directory for a new one
383 * @param directory New file to adapt. Can be NULL, meaning
384 * "no content to adapt".
385 * @param updatedStorageManager Optional updated storage manager; used to replace
386 * mStorageManager if is different (and not NULL)
387 */
388 public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
389 mFile = directory;
390 if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
391 mStorageManager = updatedStorageManager;
392 mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
393 }
394 if (mStorageManager != null) {
395 mFiles = mStorageManager.getFolderContent(mFile);
396 mFilesOrig.clear();
397 mFilesOrig.addAll(mFiles);
398
399 if (mJustFolders) {
400 mFiles = getFolders(mFiles);
401 }
402 } else {
403 mFiles = null;
404 }
405
406 mFiles = FileStorageUtils.sortFolder(mFiles);
407 notifyDataSetChanged();
408 }
409
410
411 /**
412 * Filter for getting only the folders
413 * @param files
414 * @return Vector<OCFile>
415 */
416 public Vector<OCFile> getFolders(Vector<OCFile> files) {
417 Vector<OCFile> ret = new Vector<OCFile>();
418 OCFile current = null;
419 for (int i=0; i<files.size(); i++) {
420 current = files.get(i);
421 if (current.isFolder()) {
422 ret.add(current);
423 }
424 }
425 return ret;
426 }
427
428
429 /**
430 * Check if parent folder does not include 'S' permission and if file/folder
431 * is shared with me
432 *
433 * @param file: OCFile
434 * @return boolean: True if it is shared with me and false if it is not
435 */
436 private boolean checkIfFileIsSharedWithMe(OCFile file) {
437 return (mFile.getPermissions() != null
438 && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
439 && file.getPermissions() != null
440 && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
441 }
442
443 /**
444 * Sorts list by Date
445 * @param sortAscending true: ascending, false: descending
446 */
447 private void sortByDate(boolean sortAscending){
448 final Integer val;
449 if (sortAscending){
450 val = 1;
451 } else {
452 val = -1;
453 }
454
455 Collections.sort(mFiles, new Comparator<OCFile>() {
456 public int compare(OCFile o1, OCFile o2) {
457 if (o1.isFolder() && o2.isFolder()) {
458 Long obj1 = o1.getModificationTimestamp();
459 return val * obj1.compareTo(o2.getModificationTimestamp());
460 }
461 else if (o1.isFolder()) {
462 return -1;
463 } else if (o2.isFolder()) {
464 return 1;
465 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){
466 return 0;
467 } else {
468 Long obj1 = o1.getModificationTimestamp();
469 return val * obj1.compareTo(o2.getModificationTimestamp());
470 }
471 }
472 });
473 }
474
475 /**
476 * Sorts list by Size
477 * @param sortAscending true: ascending, false: descending
478 */
479 private void sortBySize(boolean sortAscending){
480 final Integer val;
481 if (sortAscending){
482 val = 1;
483 } else {
484 val = -1;
485 }
486
487 Collections.sort(mFiles, new Comparator<OCFile>() {
488 public int compare(OCFile o1, OCFile o2) {
489 if (o1.isFolder() && o2.isFolder()) {
490 Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));
491 return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));
492 }
493 else if (o1.isFolder()) {
494 return -1;
495 } else if (o2.isFolder()) {
496 return 1;
497 } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){
498 return 0;
499 } else {
500 Long obj1 = o1.getFileLength();
501 return val * obj1.compareTo(o2.getFileLength());
502 }
503 }
504 });
505 }
506
507 /**
508 * Sorts list by Name
509 * @param sortAscending true: ascending, false: descending
510 */
511 private void sortByName(boolean sortAscending){
512 final Integer val;
513 if (sortAscending){
514 val = 1;
515 } else {
516 val = -1;
517 }
518
519 Collections.sort(mFiles, new Comparator<OCFile>() {
520 public int compare(OCFile o1, OCFile o2) {
521 if (o1.isFolder() && o2.isFolder()) {
522 return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase());
523 } else if (o1.isFolder()) {
524 return -1;
525 } else if (o2.isFolder()) {
526 return 1;
527 }
528 return val * new AlphanumComparator().compare(o1, o2);
529 }
530 });
531 }
532
533 public void setSortOrder(Integer order, boolean ascending) {
534 SharedPreferences.Editor editor = mAppPreferences.edit();
535 editor.putInt("sortOrder", order);
536 editor.putBoolean("sortAscending", ascending);
537 editor.commit();
538
539 FileStorageUtils.mSortOrder = order;
540 FileStorageUtils.mSortAscending = ascending;
541
542
543 mFiles = FileStorageUtils.sortFolder(mFiles);
544 notifyDataSetChanged();
545
546 }
547
548 private CharSequence showRelativeTimestamp(OCFile file){
549 return DisplayUtils.getRelativeDateTimeString(mContext, file.getModificationTimestamp(),
550 DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);
551 }
552 }