76dc4985a944929259a51cca313c5db96d63b273
[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 { //Folder
253 fileSizeV.setVisibility(View.INVISIBLE);
254 localStateView.setVisibility(View.INVISIBLE);
255 }
256 break;
257 case GRID_ITEM:
258 fileName = (TextView) view.findViewById(R.id.Filename);
259 name = file.getFileName();
260 fileName.setText(name);
261 break;
262 case GRID_IMAGE:
263 break;
264 }
265
266 // For all Views
267
268 // this if-else is needed even though favorite icon is visible by default
269 // because android reuses views in listview
270 if (!file.keepInSync()) {
271 view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);
272 } else {
273 view.findViewById(R.id.favoriteIcon).setVisibility(View.VISIBLE);
274 }
275
276 // No Folder
277 if (!file.isFolder()) {
278 if (file.isImage() && file.getRemoteId() != null){
279 // Thumbnail in Cache?
280 Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
281 String.valueOf(file.getRemoteId())
282 );
283 if (thumbnail != null && !file.needsUpdateThumbnail()){
284 fileIcon.setImageBitmap(thumbnail);
285 } else {
286 // generate new Thumbnail
287 if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {
288 final ThumbnailsCacheManager.ThumbnailGenerationTask task =
289 new ThumbnailsCacheManager.ThumbnailGenerationTask(
290 fileIcon, mStorageManager, mAccount
291 );
292 if (thumbnail == null) {
293 thumbnail = ThumbnailsCacheManager.mDefaultImg;
294 }
295 final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
296 new ThumbnailsCacheManager.AsyncDrawable(
297 mContext.getResources(),
298 thumbnail,
299 task
300 );
301 fileIcon.setImageDrawable(asyncDrawable);
302 task.execute(file);
303 }
304 }
305 } else {
306 fileIcon.setImageResource(DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName()));
307 }
308 } else {
309 // Folder
310 if (checkIfFileIsSharedWithMe(file)) {
311 fileIcon.setImageResource(R.drawable.shared_with_me_folder);
312 } else if (file.isShareByLink()) {
313 // If folder is sharedByLink, icon folder must be changed to
314 // folder-public one
315 fileIcon.setImageResource(R.drawable.folder_public);
316 } else {
317 fileIcon.setImageResource(
318 DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName())
319 );
320 }
321 }
322 }
323
324 return view;
325 }
326
327 /**
328 * Local Folder size in human readable format
329 *
330 * @param path
331 * String
332 * @return Size in human readable format
333 */
334 private String getFolderSizeHuman(String path) {
335
336 File dir = new File(path);
337
338 if (dir.exists()) {
339 long bytes = FileStorageUtils.getFolderSize(dir);
340 return DisplayUtils.bytesToHumanReadable(bytes);
341 }
342
343 return "0 B";
344 }
345
346 /**
347 * Local Folder size
348 * @param dir File
349 * @return Size in bytes
350 */
351 private long getFolderSize(File dir) {
352 if (dir.exists()) {
353 long result = 0;
354 File[] fileList = dir.listFiles();
355 for(int i = 0; i < fileList.length; i++) {
356 if(fileList[i].isDirectory()) {
357 result += getFolderSize(fileList[i]);
358 } else {
359 result += fileList[i].length();
360 }
361 }
362 return result;
363 }
364 return 0;
365 }
366
367 @Override
368 public int getViewTypeCount() {
369 return 1;
370 }
371
372 @Override
373 public boolean hasStableIds() {
374 return true;
375 }
376
377 @Override
378 public boolean isEmpty() {
379 return (mFiles == null || mFiles.isEmpty());
380 }
381
382 /**
383 * Change the adapted directory for a new one
384 * @param directory New file to adapt. Can be NULL, meaning
385 * "no content to adapt".
386 * @param updatedStorageManager Optional updated storage manager; used to replace
387 * mStorageManager if is different (and not NULL)
388 */
389 public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
390 mFile = directory;
391 if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
392 mStorageManager = updatedStorageManager;
393 mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
394 }
395 if (mStorageManager != null) {
396 mFiles = mStorageManager.getFolderContent(mFile);
397 mFilesOrig.clear();
398 mFilesOrig.addAll(mFiles);
399
400 if (mJustFolders) {
401 mFiles = getFolders(mFiles);
402 }
403 } else {
404 mFiles = null;
405 }
406
407 mFiles = FileStorageUtils.sortFolder(mFiles);
408 notifyDataSetChanged();
409 }
410
411
412 /**
413 * Filter for getting only the folders
414 * @param files
415 * @return Vector<OCFile>
416 */
417 public Vector<OCFile> getFolders(Vector<OCFile> files) {
418 Vector<OCFile> ret = new Vector<OCFile>();
419 OCFile current = null;
420 for (int i=0; i<files.size(); i++) {
421 current = files.get(i);
422 if (current.isFolder()) {
423 ret.add(current);
424 }
425 }
426 return ret;
427 }
428
429
430 /**
431 * Check if parent folder does not include 'S' permission and if file/folder
432 * is shared with me
433 *
434 * @param file: OCFile
435 * @return boolean: True if it is shared with me and false if it is not
436 */
437 private boolean checkIfFileIsSharedWithMe(OCFile file) {
438 return (mFile.getPermissions() != null
439 && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
440 && file.getPermissions() != null
441 && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
442 }
443
444 /**
445 * Sorts list by Date
446 * @param sortAscending true: ascending, false: descending
447 */
448 private void sortByDate(boolean sortAscending){
449 final Integer val;
450 if (sortAscending){
451 val = 1;
452 } else {
453 val = -1;
454 }
455
456 Collections.sort(mFiles, new Comparator<OCFile>() {
457 public int compare(OCFile o1, OCFile o2) {
458 if (o1.isFolder() && o2.isFolder()) {
459 Long obj1 = o1.getModificationTimestamp();
460 return val * obj1.compareTo(o2.getModificationTimestamp());
461 }
462 else if (o1.isFolder()) {
463 return -1;
464 } else if (o2.isFolder()) {
465 return 1;
466 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){
467 return 0;
468 } else {
469 Long obj1 = o1.getModificationTimestamp();
470 return val * obj1.compareTo(o2.getModificationTimestamp());
471 }
472 }
473 });
474 }
475
476 /**
477 * Sorts list by Size
478 * @param sortAscending true: ascending, false: descending
479 */
480 private void sortBySize(boolean sortAscending){
481 final Integer val;
482 if (sortAscending){
483 val = 1;
484 } else {
485 val = -1;
486 }
487
488 Collections.sort(mFiles, new Comparator<OCFile>() {
489 public int compare(OCFile o1, OCFile o2) {
490 if (o1.isFolder() && o2.isFolder()) {
491 Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));
492 return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));
493 }
494 else if (o1.isFolder()) {
495 return -1;
496 } else if (o2.isFolder()) {
497 return 1;
498 } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){
499 return 0;
500 } else {
501 Long obj1 = o1.getFileLength();
502 return val * obj1.compareTo(o2.getFileLength());
503 }
504 }
505 });
506 }
507
508 /**
509 * Sorts list by Name
510 * @param sortAscending true: ascending, false: descending
511 */
512 private void sortByName(boolean sortAscending){
513 final Integer val;
514 if (sortAscending){
515 val = 1;
516 } else {
517 val = -1;
518 }
519
520 Collections.sort(mFiles, new Comparator<OCFile>() {
521 public int compare(OCFile o1, OCFile o2) {
522 if (o1.isFolder() && o2.isFolder()) {
523 return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase());
524 } else if (o1.isFolder()) {
525 return -1;
526 } else if (o2.isFolder()) {
527 return 1;
528 }
529 return val * new AlphanumComparator().compare(o1, o2);
530 }
531 });
532 }
533
534 public void setSortOrder(Integer order, boolean ascending) {
535 SharedPreferences.Editor editor = mAppPreferences.edit();
536 editor.putInt("sortOrder", order);
537 editor.putBoolean("sortAscending", ascending);
538 editor.commit();
539
540 FileStorageUtils.mSortOrder = order;
541 FileStorageUtils.mSortAscending = ascending;
542
543
544 mFiles = FileStorageUtils.sortFolder(mFiles);
545 notifyDataSetChanged();
546
547 }
548
549 private CharSequence showRelativeTimestamp(OCFile file){
550 return DisplayUtils.getRelativeDateTimeString(mContext, file.getModificationTimestamp(),
551 DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);
552 }
553 }