d2680fa581a2b9edfdc14f9f7730d2d6f65fa968
[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 <<<<<<< HEAD
30 import android.graphics.Bitmap;
31 import android.graphics.BitmapFactory;
32 import android.media.ThumbnailUtils;
33 =======
34 import android.content.SharedPreferences;
35 import android.graphics.Bitmap;
36 import android.preference.PreferenceManager;
37 >>>>>>> upstream/develop
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.widget.BaseAdapter;
42 import android.widget.GridView;
43 import android.widget.ImageView;
44 import android.widget.ListAdapter;
45 import android.widget.ListView;
46 import android.widget.TextView;
47
48 import com.owncloud.android.R;
49 import com.owncloud.android.authentication.AccountUtils;
50 import com.owncloud.android.datamodel.FileDataStorageManager;
51 import com.owncloud.android.datamodel.OCFile;
52 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
53 import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable;
54 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
55 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
56 import com.owncloud.android.ui.activity.ComponentsGetter;
57 import com.owncloud.android.utils.DisplayUtils;
58 <<<<<<< HEAD
59 import com.owncloud.android.utils.Log_OC;
60
61 =======
62 import com.owncloud.android.utils.FileStorageUtils;
63
64 >>>>>>> upstream/develop
65
66 /**
67 * This Adapter populates a ListView with all files and folders in an ownCloud
68 * instance.
69 *
70 * @author Bartek Przybylski
71 * @author Tobias Kaminsky
72 * @author David A. Velasco
73 */
74 public class FileListListAdapter extends BaseAdapter implements ListAdapter {
75 private final static String PERMISSION_SHARED_WITH_ME = "S";
76
77 private Context mContext;
78 private OCFile mFile = null;
79 private Vector<OCFile> mFiles = null;
80 private boolean mJustFolders;
81
82 private FileDataStorageManager mStorageManager;
83 private Account mAccount;
84 private ComponentsGetter mTransferServiceGetter;
85 <<<<<<< HEAD
86 private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM };
87 =======
88 private Integer mSortOrder;
89 public static final Integer SORT_NAME = 0;
90 public static final Integer SORT_DATE = 1;
91 public static final Integer SORT_SIZE = 2;
92 private Boolean mSortAscending;
93 private SharedPreferences mAppPreferences;
94 >>>>>>> upstream/develop
95
96 public FileListListAdapter(
97 boolean justFolders,
98 Context context,
99 ComponentsGetter transferServiceGetter
100 ) {
101
102 mJustFolders = justFolders;
103 mContext = context;
104 mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
105
106 mTransferServiceGetter = transferServiceGetter;
107
108 mAppPreferences = PreferenceManager
109 .getDefaultSharedPreferences(mContext);
110
111 // Read sorting order, default to sort by name ascending
112 mSortOrder = mAppPreferences
113 .getInt("sortOrder", 0);
114 mSortAscending = mAppPreferences.getBoolean("sortAscending", true);
115
116 // initialise thumbnails cache on background thread
117 new ThumbnailsCacheManager.InitDiskCacheTask().execute();
118
119 }
120
121 @Override
122 public boolean areAllItemsEnabled() {
123 return true;
124 }
125
126 @Override
127 public boolean isEnabled(int position) {
128 return true;
129 }
130
131 @Override
132 public int getCount() {
133 return mFiles != null ? mFiles.size() : 0;
134 }
135
136 @Override
137 public Object getItem(int position) {
138 if (mFiles == null || mFiles.size() <= position)
139 return null;
140 return mFiles.get(position);
141 }
142
143 @Override
144 public long getItemId(int position) {
145 if (mFiles == null || mFiles.size() <= position)
146 return 0;
147 return mFiles.get(position).getFileId();
148 }
149
150 @Override
151 public int getItemViewType(int position) {
152 return 0;
153 }
154
155 @Override
156 public View getView(int position, View convertView, ViewGroup parent) {
157 // decide image vs. file view
158 double count = 0;
159
160
161 for (OCFile file : mFiles){
162 if (file.isImage()){
163 count++;
164 }
165 }
166
167 // TODO threshold as constant in Preferences
168 // > 50% Images --> image view
169 boolean fileView = true;
170 if ((count / mFiles.size()) >= 0.5){
171 fileView = false;
172 } else {
173 fileView = true;
174 }
175
176 View view = convertView;
177 OCFile file = null;
178 LayoutInflater inflator = (LayoutInflater) mContext
179 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
180
181 if (mFiles != null && mFiles.size() > position) {
182 file = mFiles.get(position);
183 }
184
185 // Find out which layout should be displayed
186 ViewType viewType;
187 if (fileView){
188 viewType = ViewType.LIST_ITEM;
189 } else if (file.isImage()){
190 viewType = ViewType.GRID_IMAGE;
191 } else {
192 viewType = ViewType.GRID_ITEM;
193 }
194
195 // Create View
196 switch (viewType){
197 case GRID_IMAGE:
198 view = inflator.inflate(R.layout.grid_image, null);
199 break;
200 case GRID_ITEM:
201 view = inflator.inflate(R.layout.grid_item, null);
202 break;
203 case LIST_ITEM:
204 view = inflator.inflate(R.layout.list_item, null);
205 break;
206 }
207 <<<<<<< HEAD
208
209 view.invalidate();
210
211 if (file != null){
212
213 ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
214 TextView fileName;
215 String name;
216 =======
217
218 if (mFiles != null && mFiles.size() > position) {
219 OCFile file = mFiles.get(position);
220 TextView fileName = (TextView) view.findViewById(R.id.Filename);
221 String name = file.getFileName();
222
223 fileName.setText(name);
224 ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
225 fileIcon.setTag(file.getFileId());
226 ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);
227 ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);
228 sharedWithMeIconV.setVisibility(View.GONE);
229
230 ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);
231 localStateView.bringToFront();
232 FileDownloaderBinder downloaderBinder =
233 mTransferServiceGetter.getFileDownloaderBinder();
234 FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
235 if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
236 localStateView.setImageResource(R.drawable.downloading_file_indicator);
237 localStateView.setVisibility(View.VISIBLE);
238 } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
239 localStateView.setImageResource(R.drawable.uploading_file_indicator);
240 localStateView.setVisibility(View.VISIBLE);
241 } else if (file.isDown()) {
242 localStateView.setImageResource(R.drawable.local_file_indicator);
243 localStateView.setVisibility(View.VISIBLE);
244 } else {
245 localStateView.setVisibility(View.INVISIBLE);
246 }
247
248 TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
249 TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
250 ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
251 >>>>>>> upstream/develop
252
253 switch (viewType){
254 case LIST_ITEM:
255 fileName = (TextView) view.findViewById(R.id.Filename);
256 name = file.getFileName();
257 fileName.setText(name);
258
259 TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
260 TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
261 ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
262
263 lastModV.setVisibility(View.VISIBLE);
264 <<<<<<< HEAD
265 lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));
266
267 checkBoxV.setVisibility(View.GONE);
268
269 fileSizeV.setVisibility(View.VISIBLE);
270 fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
271
272 ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);
273
274
275 if (file.isShareByLink()) {
276 sharedIconV.setVisibility(View.VISIBLE);
277 =======
278 lastModV.setText(
279 DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())
280 );
281 // this if-else is needed even thoe fav icon is visible by default
282 // because android reuses views in listview
283 if (!file.keepInSync()) {
284 view.findViewById(R.id.imageView3).setVisibility(View.GONE);
285 >>>>>>> upstream/develop
286 } else {
287 sharedIconV.setVisibility(View.GONE);
288 }
289
290 ImageView localStateView = (ImageView) view.findViewById(R.id.localFileIndicator);
291
292 if (!file.isFolder()) {
293 GridView parentList = (GridView)parent;
294 if (parentList.getChoiceMode() == GridView.CHOICE_MODE_NONE) {
295 checkBoxV.setVisibility(View.GONE);
296 } else {
297 if (parentList.isItemChecked(position)) {
298 checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
299 } else {
300 checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
301 }
302 checkBoxV.setVisibility(View.VISIBLE);
303 }
304 <<<<<<< HEAD
305
306 localStateView.bringToFront();
307 FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
308 FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
309 if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
310 localStateView.setImageResource(R.drawable.downloading_file_indicator);
311 localStateView.setVisibility(View.VISIBLE);
312 } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
313 localStateView.setImageResource(R.drawable.uploading_file_indicator);
314 localStateView.setVisibility(View.VISIBLE);
315 } else if (file.isDown()) {
316 localStateView.setImageResource(R.drawable.local_file_indicator);
317 localStateView.setVisibility(View.VISIBLE);
318 } else {
319 localStateView.setVisibility(View.INVISIBLE);
320 }
321
322 ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);
323 if (checkIfFileIsSharedWithMe(file)) {
324 sharedWithMeIconV.setVisibility(View.VISIBLE);
325 } else {
326 sharedWithMeIconV.setVisibility(View.GONE);
327 }
328 } else {
329 localStateView.setVisibility(View.INVISIBLE);
330 }
331 break;
332 case GRID_ITEM:
333 fileName = (TextView) view.findViewById(R.id.Filename);
334 name = file.getFileName();
335 fileName.setText(name);
336 break;
337 case GRID_IMAGE:
338 break;
339 }
340
341 // For all Views
342
343 // this if-else is needed even though favorite icon is visible by default
344 // because android reuses views in listview
345 if (!file.keepInSync()) {
346 view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);
347 } else {
348 view.findViewById(R.id.favoriteIcon).setVisibility(View.VISIBLE);
349 }
350
351 // No Folder
352 if (!file.isFolder()) {
353 if (file.isImage() && file.isDown()){
354 Bitmap bitmap = BitmapFactory.decodeFile(file.getStoragePath());
355 fileIcon.setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap, 200, 200));
356 } else {
357 fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));
358 }
359 } else {
360 // Folder
361 =======
362 checkBoxV.setVisibility(View.VISIBLE);
363 }
364
365 // get Thumbnail if file is image
366 if (file.isImage() && file.getRemoteId() != null){
367 // Thumbnail in Cache?
368 Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
369 String.valueOf(file.getRemoteId())
370 );
371 if (thumbnail != null && !file.needsUpdateThumbnail()){
372 fileIcon.setImageBitmap(thumbnail);
373 } else {
374 // generate new Thumbnail
375 if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {
376 final ThumbnailsCacheManager.ThumbnailGenerationTask task =
377 new ThumbnailsCacheManager.ThumbnailGenerationTask(
378 fileIcon, mStorageManager, mAccount
379 );
380 if (thumbnail == null) {
381 thumbnail = ThumbnailsCacheManager.mDefaultImg;
382 }
383 final AsyncDrawable asyncDrawable = new AsyncDrawable(
384 mContext.getResources(),
385 thumbnail,
386 task
387 );
388 fileIcon.setImageDrawable(asyncDrawable);
389 task.execute(file);
390 }
391 }
392 } else {
393 fileIcon.setImageResource(
394 DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())
395 );
396 }
397
398 if (checkIfFileIsSharedWithMe(file)) {
399 sharedWithMeIconV.setVisibility(View.VISIBLE);
400 }
401 }
402 else {
403 // TODO Re-enable when server supports folder-size calculation
404 // if (FileStorageUtils.getDefaultSavePathFor(mAccount.name, file) != null){
405 // fileSizeV.setVisibility(View.VISIBLE);
406 // fileSizeV.setText(getFolderSizeHuman(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)));
407 // } else {
408 fileSizeV.setVisibility(View.INVISIBLE);
409 // }
410
411 lastModV.setVisibility(View.VISIBLE);
412 lastModV.setText(
413 DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())
414 );
415 checkBoxV.setVisibility(View.GONE);
416 view.findViewById(R.id.imageView3).setVisibility(View.GONE);
417
418 >>>>>>> upstream/develop
419 if (checkIfFileIsSharedWithMe(file)) {
420 fileIcon.setImageResource(R.drawable.shared_with_me_folder);
421 } else if (file.isShareByLink()) {
422 // If folder is sharedByLink, icon folder must be changed to
423 // folder-public one
424 fileIcon.setImageResource(R.drawable.folder_public);
425 } else {
426 fileIcon.setImageResource(
427 DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())
428 );
429 }
430 }
431 }
432
433 return view;
434 }
435
436 /**
437 * Local Folder size in human readable format
438 *
439 * @param path
440 * String
441 * @return Size in human readable format
442 */
443 private String getFolderSizeHuman(String path) {
444
445 File dir = new File(path);
446
447 if (dir.exists()) {
448 long bytes = getFolderSize(dir);
449 return DisplayUtils.bytesToHumanReadable(bytes);
450 }
451
452 return "0 B";
453 }
454
455 /**
456 * Local Folder size
457 * @param dir File
458 * @return Size in bytes
459 */
460 private long getFolderSize(File dir) {
461 if (dir.exists()) {
462 long result = 0;
463 File[] fileList = dir.listFiles();
464 for(int i = 0; i < fileList.length; i++) {
465 if(fileList[i].isDirectory()) {
466 result += getFolderSize(fileList[i]);
467 } else {
468 result += fileList[i].length();
469 }
470 }
471 return result;
472 }
473 return 0;
474 }
475
476 @Override
477 public int getViewTypeCount() {
478 return 1;
479 }
480
481 @Override
482 public boolean hasStableIds() {
483 return true;
484 }
485
486 @Override
487 public boolean isEmpty() {
488 return (mFiles == null || mFiles.isEmpty());
489 }
490
491 /**
492 * Change the adapted directory for a new one
493 * @param directory New file to adapt. Can be NULL, meaning
494 * "no content to adapt".
495 * @param updatedStorageManager Optional updated storage manager; used to replace
496 * mStorageManager if is different (and not NULL)
497 */
498 public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
499 mFile = directory;
500 if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
501 mStorageManager = updatedStorageManager;
502 mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
503 }
504 if (mStorageManager != null) {
505 mFiles = mStorageManager.getFolderContent(mFile);
506 if (mJustFolders) {
507 mFiles = getFolders(mFiles);
508 }
509 } else {
510 mFiles = null;
511 }
512
513 sortDirectory();
514 }
515
516 /**
517 * Sorts all filenames, regarding last user decision
518 */
519 private void sortDirectory(){
520 switch (mSortOrder){
521 case 0:
522 sortByName(mSortAscending);
523 break;
524 case 1:
525 sortByDate(mSortAscending);
526 break;
527 case 2:
528 sortBySize(mSortAscending);
529 break;
530 }
531
532 notifyDataSetChanged();
533 }
534
535
536 /**
537 * Filter for getting only the folders
538 * @param files
539 * @return Vector<OCFile>
540 */
541 public Vector<OCFile> getFolders(Vector<OCFile> files) {
542 Vector<OCFile> ret = new Vector<OCFile>();
543 OCFile current = null;
544 for (int i=0; i<files.size(); i++) {
545 current = files.get(i);
546 if (current.isFolder()) {
547 ret.add(current);
548 }
549 }
550 return ret;
551 }
552
553
554 /**
555 * Check if parent folder does not include 'S' permission and if file/folder
556 * is shared with me
557 *
558 * @param file: OCFile
559 * @return boolean: True if it is shared with me and false if it is not
560 */
561 private boolean checkIfFileIsSharedWithMe(OCFile file) {
562 return (mFile.getPermissions() != null
563 && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
564 && file.getPermissions() != null
565 && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
566 }
567
568 /**
569 * Sorts list by Date
570 * @param sortAscending true: ascending, false: descending
571 */
572 private void sortByDate(boolean sortAscending){
573 final Integer val;
574 if (sortAscending){
575 val = 1;
576 } else {
577 val = -1;
578 }
579
580 Collections.sort(mFiles, new Comparator<OCFile>() {
581 public int compare(OCFile o1, OCFile o2) {
582 if (o1.isFolder() && o2.isFolder()) {
583 Long obj1 = o1.getModificationTimestamp();
584 return val * obj1.compareTo(o2.getModificationTimestamp());
585 }
586 else if (o1.isFolder()) {
587 return -1;
588 } else if (o2.isFolder()) {
589 return 1;
590 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){
591 return 0;
592 } else {
593 Long obj1 = o1.getModificationTimestamp();
594 return val * obj1.compareTo(o2.getModificationTimestamp());
595 }
596 }
597 });
598 }
599
600 /**
601 * Sorts list by Size
602 * @param sortAscending true: ascending, false: descending
603 */
604 private void sortBySize(boolean sortAscending){
605 final Integer val;
606 if (sortAscending){
607 val = 1;
608 } else {
609 val = -1;
610 }
611
612 Collections.sort(mFiles, new Comparator<OCFile>() {
613 public int compare(OCFile o1, OCFile o2) {
614 if (o1.isFolder() && o2.isFolder()) {
615 Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));
616 return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));
617 }
618 else if (o1.isFolder()) {
619 return -1;
620 } else if (o2.isFolder()) {
621 return 1;
622 } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){
623 return 0;
624 } else {
625 Long obj1 = o1.getFileLength();
626 return val * obj1.compareTo(o2.getFileLength());
627 }
628 }
629 });
630 }
631
632 /**
633 * Sorts list by Name
634 * @param sortAscending true: ascending, false: descending
635 */
636 private void sortByName(boolean sortAscending){
637 final Integer val;
638 if (sortAscending){
639 val = 1;
640 } else {
641 val = -1;
642 }
643
644 Collections.sort(mFiles, new Comparator<OCFile>() {
645 public int compare(OCFile o1, OCFile o2) {
646 if (o1.isFolder() && o2.isFolder()) {
647 return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase());
648 } else if (o1.isFolder()) {
649 return -1;
650 } else if (o2.isFolder()) {
651 return 1;
652 }
653 return val * new AlphanumComparator().compare(o1, o2);
654 }
655 });
656 }
657
658 public void setSortOrder(Integer order, boolean ascending) {
659 SharedPreferences.Editor editor = mAppPreferences.edit();
660 editor.putInt("sortOrder", order);
661 editor.putBoolean("sortAscending", ascending);
662 editor.commit();
663
664 mSortOrder = order;
665 mSortAscending = ascending;
666
667 sortDirectory();
668 }
669 }