From: jabarros Date: Thu, 9 Oct 2014 15:26:02 +0000 (+0200) Subject: Merge branch 'sortingFiles' of https://github.com/tobiasKaminsky/android into sorting... X-Git-Tag: oc-android-1.7.0_signed~134^2~5 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/7d9fc6857e1d534c1930b94b6063f1e21a827e9b Merge branch 'sortingFiles' of https://github.com/tobiasKaminsky/android into sorting_files Conflicts: res/menu/main_menu.xml src/com/owncloud/android/ui/activity/FileDisplayActivity.java src/com/owncloud/android/ui/adapter/FileListListAdapter.java --- 7d9fc6857e1d534c1930b94b6063f1e21a827e9b diff --cc res/menu/main_menu.xml index 1df273ea,506ed15c..6e64fcf7 --- a/res/menu/main_menu.xml +++ b/res/menu/main_menu.xml @@@ -44,11 -44,10 +44,16 @@@ android:showAsAction="never" android:title="@string/actionbar_settings"/> ++ diff --cc src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 21472fd1,2d10ab94..8560ddbb --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@@ -48,7 -50,8 +48,6 @@@ import android.provider.MediaStore import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v4.widget.SwipeRefreshLayout; --import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@@ -1748,4 -1757,17 +1779,16 @@@ OnSslUntrustedCertListener, OnEnforceab } } } - ++ + private void sortByDate(boolean ascending){ + getListOfFilesFragment().sortByDate(ascending); + } + + private void sortBySize(boolean ascending){ + getListOfFilesFragment().sortBySize(ascending); + } + + private void sortByName(boolean ascending){ + getListOfFilesFragment().sortByName(ascending); + } - } diff --cc src/com/owncloud/android/ui/adapter/FileListListAdapter.java index ffdad175,a90773ef..ffc2d69f --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@@ -18,20 -18,14 +18,24 @@@ package com.owncloud.android.ui.adapter; import java.io.File; +import java.lang.ref.WeakReference; + import java.util.Collections; + import java.util.Comparator; import java.util.Vector; import android.accounts.Account; import android.content.Context; + import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.ThumbnailUtils; +import android.os.AsyncTask; + import android.preference.PreferenceManager; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@@ -47,10 -42,10 +52,12 @@@ import com.owncloud.android.datamodel.F import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.ComponentsGetter; +import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; + import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.Log_OC; ++ /** @@@ -74,15 -66,10 +81,18 @@@ public class FileListListAdapter extend private FileDataStorageManager mStorageManager; private Account mAccount; private ComponentsGetter mTransferServiceGetter; + private Integer sortOrder; + private Boolean sortAscending; + private SharedPreferences appPreferences; + private final Object thumbnailDiskCacheLock = new Object(); + private DiskLruImageCache mThumbnailCache; + private boolean mThumbnailCacheStarting = true; + private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB + private static final CompressFormat mCompressFormat = CompressFormat.JPEG; + private static final int mCompressQuality = 70; + private Bitmap defaultImg; + public FileListListAdapter( boolean justFolders, Context context, @@@ -93,141 -79,15 +103,150 @@@ mContext = context; mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext); mTransferServiceGetter = transferServiceGetter; + + appPreferences = PreferenceManager + .getDefaultSharedPreferences(mContext); + + // Read sorting order, default to sort by name ascending + sortOrder = appPreferences + .getInt("sortOrder", 0); + sortAscending = appPreferences.getBoolean("sortAscending", true); + + defaultImg = BitmapFactory.decodeResource(mContext.getResources(), + DisplayUtils.getResourceId("image/png", "default.png")); + + // Initialise disk cache on background thread + new InitDiskCacheTask().execute(); + } + + class InitDiskCacheTask extends AsyncTask { + @Override + protected Void doInBackground(File... params) { + synchronized (thumbnailDiskCacheLock) { + try { + mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", + DISK_CACHE_SIZE, mCompressFormat, mCompressQuality); + } catch (Exception e) { + Log_OC.d(TAG, "Thumbnail cache could not be opened ", e); + mThumbnailCache = null; + } + mThumbnailCacheStarting = false; // Finished initialization + thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads + } + return null; + } + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncDrawable(Resources res, Bitmap bitmap, + ThumbnailGenerationTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = + new WeakReference(bitmapWorkerTask); + } + + public ThumbnailGenerationTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + class ThumbnailGenerationTask extends AsyncTask { + private final WeakReference imageViewReference; + private OCFile file; + + + public ThumbnailGenerationTask(ImageView imageView) { + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference(imageView); + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(OCFile... params) { + Bitmap thumbnail = null; + + try { + file = params[0]; + final String imageKey = String.valueOf(file.getRemoteId()); + + // Check disk cache in background thread + thumbnail = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (thumbnail == null || file.needsUpdateThumbnail()) { + // Converts dp to pixel + Resources r = mContext.getResources(); + int px = (int) Math.round(TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics() + )); + + if (file.isDown()){ + Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( + file.getStoragePath(), px, px); + + if (bitmap != null) { + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Add thumbnail to cache + addBitmapToCache(imageKey, thumbnail); + + file.setNeedsUpdateThumbnail(false); + mStorageManager.saveFile(file); + } + + } + } + + } catch (Throwable t) { + // the app should never break due to a problem with thumbnails + Log_OC.e(TAG, "Generation of thumbnail for " + file + " failed", t); + if (t instanceof OutOfMemoryError) { + System.gc(); + } + } + + return thumbnail; + } + + protected void onPostExecute(Bitmap bitmap){ + if (isCancelled()) { + bitmap = null; + } + + if (imageViewReference != null && bitmap != null) { + final ImageView imageView = imageViewReference.get(); + final ThumbnailGenerationTask bitmapWorkerTask = + getBitmapWorkerTask(imageView); + if (this == bitmapWorkerTask && imageView != null) { + imageView.setImageBitmap(bitmap); + } + } + } + } + + public void addBitmapToCache(String key, Bitmap bitmap) { + synchronized (thumbnailDiskCacheLock) { + if (mThumbnailCache != null) { + mThumbnailCache.put(key, bitmap); + } + } + } + + public Bitmap getBitmapFromDiskCache(String key) { + synchronized (thumbnailDiskCacheLock) { + // Wait while disk cache is started from background thread + while (mThumbnailCacheStarting) { + try { + thumbnailDiskCacheLock.wait(); + } catch (InterruptedException e) {} + } + if (mThumbnailCache != null) { + return (Bitmap) mThumbnailCache.getBitmap(key); + } + } - return null; ++ return null; } @Override @@@ -361,12 -197,15 +380,17 @@@ } } else { - fileSizeV.setVisibility(View.INVISIBLE); - //fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); + if (FileStorageUtils.getDefaultSavePathFor(mAccount.name, file) != null){ + fileSizeV.setVisibility(View.VISIBLE); + fileSizeV.setText(getFolderSizeHuman(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file))); + } else { + fileSizeV.setVisibility(View.INVISIBLE); + } - ++ lastModV.setVisibility(View.VISIBLE); - lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())); + lastModV.setText( + DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()) + ); checkBoxV.setVisibility(View.GONE); view.findViewById(R.id.imageView3).setVisibility(View.GONE); @@@ -395,35 -232,48 +419,77 @@@ return view; } - ++ + public static boolean cancelPotentialWork(OCFile file, ImageView imageView) { + final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final OCFile bitmapData = bitmapWorkerTask.file; + // If bitmapData is not yet set or it differs from the new data + if (bitmapData == null || bitmapData != file) { + // Cancel previous task + bitmapWorkerTask.cancel(true); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } ++ + /** + * Local Folder size in human readable format + * @param path String + * @return Size in human readable format + */ + private String getFolderSizeHuman(String path) { + + File dir = new File(path); + + if(dir.exists()) { + long bytes = getFolderSize(dir); + if (bytes < 1024) return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(1024)); + String pre = ("KMGTPE").charAt(exp-1) + ""; + + return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre); + } + + return "0 B"; + } + + /** + * Local Folder size + * @param dir File + * @return Size in bytes + */ + private long getFolderSize(File dir) { + if (dir.exists()) { + long result = 0; + File[] fileList = dir.listFiles(); + for(int i = 0; i < fileList.length; i++) { + if(fileList[i].isDirectory()) { + result += getFolderSize(fileList[i]); + } else { + result += fileList[i].length(); + } + } + return result; + } + return 0; - } ++ } @Override public int getViewTypeCount() { @@@ -491,9 -359,106 +577,108 @@@ * @return boolean: True if it is shared with me and false if it is not */ private boolean checkIfFileIsSharedWithMe(OCFile file) { - return (mFile.getPermissions() != null && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME) - && file.getPermissions() != null && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME)); + return (mFile.getPermissions() != null + && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME) + && file.getPermissions() != null + && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME)); } + + /** + * Sorts list by Date + * @param sortAscending true: ascending, false: descending + */ + private void sortByDate(boolean sortAscending){ + final Integer val; + if (sortAscending){ + val = 1; + } else { + val = -1; + } + + Collections.sort(mFiles, new Comparator() { + public int compare(OCFile o1, OCFile o2) { + if (o1.isFolder() && o2.isFolder()) { + return val * Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp()); + } + else if (o1.isFolder()) { + return -1; + } else if (o2.isFolder()) { + return 1; + } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){ + return 0; + } else { + return val * Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp()); + } + } + }); + } + + /** + * Sorts list by Size + * @param sortAscending true: ascending, false: descending + */ + private void sortBySize(boolean sortAscending){ + final Integer val; + if (sortAscending){ + val = 1; + } else { + val = -1; + } + + Collections.sort(mFiles, new Comparator() { + public int compare(OCFile o1, OCFile o2) { + if (o1.isFolder() && o2.isFolder()) { + return val * Long.compare(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1))), + getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2)))); + } + else if (o1.isFolder()) { + return -1; + } else if (o2.isFolder()) { + return 1; + } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){ + return 0; + } else { + return val * Long.compare(o1.getFileLength(), o2.getFileLength()); + } + } + }); + } + + /** + * Sorts list by Name + * @param sortAscending true: ascending, false: descending + */ + private void sortByName(boolean sortAscending){ + final Integer val; + if (sortAscending){ + val = 1; + } else { + val = -1; + } + + Collections.sort(mFiles, new Comparator() { + public int compare(OCFile o1, OCFile o2) { + if (o1.isFolder() && o2.isFolder()) { + return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase()); + } else if (o1.isFolder()) { + return -1; + } else if (o2.isFolder()) { + return 1; + } + return val * new AlphanumComparator().compare(o1, o2); + } + }); + } + + public void setSortOrder(Integer order, boolean ascending) { + SharedPreferences.Editor editor = appPreferences.edit(); + editor.putInt("sortOrder", order); + editor.putBoolean("sortAscending", ascending); + editor.commit(); + + sortOrder = order; + sortAscending = ascending; + + sortDirectory(); + } }