Merge branch 'sortingFiles' of https://github.com/tobiasKaminsky/android into sorting...
authorjabarros <jabarros@solidgear.es>
Thu, 9 Oct 2014 15:26:02 +0000 (17:26 +0200)
committerjabarros <jabarros@solidgear.es>
Thu, 9 Oct 2014 15:26:02 +0000 (17:26 +0200)
Conflicts:
res/menu/main_menu.xml
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java

1  2 
res/menu/main_menu.xml
res/values-de/strings.xml
res/values/strings.xml
src/com/owncloud/android/datamodel/OCFile.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

diff --combined res/menu/main_menu.xml
          android:showAsAction="never"
          android:title="@string/actionbar_settings"/>
      <item
 +        android:id="@+id/action_logger"
 +        android:icon="@drawable/ic_action_settings"
 +        android:orderInCategory="2"
 +        android:showAsAction="never"
 +        android:title="@string/actionbar_logger"/>
++      <item
+         android:id="@+id/action_sort"
+         android:orderInCategory="2"
+         android:showAsAction="never"
+         android:title="@string/actionbar_sort"/>
  
      <!-- <item android:id="@+id/search" android:title="@string/actionbar_search" android:icon="@drawable/ic_action_search"></item> -->
  
    <string name="actionbar_settings">Einstellungen</string>
    <string name="actionbar_see_details">Details</string>
    <string name="actionbar_send_file">Senden</string>
+   <string name="actionbar_sort">Sortieren</string>
+   <string name="actionbar_sort_title">Sortieren nach</string>
+   <string-array name="actionbar_sortby">
+       <item>A-Z</item>
+       <item>Neu - Alt</item>
+       <item>Groß - Klein</item>
+   </string-array>
    <string name="prefs_category_general">Allgemein</string>
    <string name="prefs_category_more">Mehr</string>
    <string name="prefs_accounts">Konten</string>
    <string name="prefs_category_accounts">Konten</string>
    <string name="prefs_add_account">Konto hinzufügen</string>
    <string name="auth_redirect_non_secure_connection_title">Die gesicherte Verbindung wird auf eine unsichere Route weitergeleitet.</string>
 -  <string name="saml_authentication_required_text">Authentifizierung benötigt</string>
 +  <string name="actionbar_logger">Protokolle</string>
 +  <string name="log_send_history_button">Verlauf senden</string>
 +  <string name="log_mail_subject">Protokolle der ownCloud-Android-App</string>
 +  <string name="log_progress_dialog_text">Daten werden geladen …</string>
 +  <string name="saml_authentication_required_text">Legitimierung benötigt</string>
    <string name="saml_authentication_wrong_pass">Falsches Passwort</string>
    <string name="actionbar_move">Verschieben</string>
    <string name="file_list_empty_moving">Nichts vorhanden. Du kannst einen Ordner hinzufügen!</string>
    <string name="move_choose_button_text">Auswählen</string>
    <string name="move_file_not_found">Verschieben nicht möglich. Prüfe, dass die Datei existiert</string>
    <string name="move_file_invalid_into_descendent">Es ist nicht möglich einen Ordner eine Ebene tiefer zu verschieben</string>
 -  <string name="move_file_invalid_overwrite">Die Datei existiert bereits im Zielordner</string>
 +  <string name="move_file_invalid_overwrite">Die Datei ist bereits im Zielordner vorhanden</string>
    <string name="move_file_error">Es ist ein Fehler beim Verschieben dieser Datei oder Ordners aufgetreten.</string>
    <string name="forbidden_permissions_move">zum Datei verschieben</string>
  </resources>
diff --combined res/values/strings.xml
      <string name="actionbar_settings">Settings</string>
      <string name="actionbar_see_details">Details</string>
      <string name="actionbar_send_file">Send</string>
+     <string name="actionbar_sort">Sort</string>
+     <string name="actionbar_sort_title">Sort by</string>
+     <string-array name="actionbar_sortby">
+       <item>A-Z</item>
+       <item>Newest - Oldest</item>
+       <item>Biggest - Smallest</item>
+     </string-array>
      <string name="prefs_category_general">General</string>
      <string name="prefs_category_more">More</string>
      <string name="prefs_accounts">Accounts</string>
        <string name="prefs_add_account">Add account</string>
        <string name="auth_redirect_non_secure_connection_title">Secure connection is redirected through an unsecured route.</string>
  
 +      <string name="actionbar_logger">Logs</string>
 +      <string name="log_send_history_button">Send History</string>
 +      <string name="log_mail_subject">ownCloud Android app logs</string>
 +      <string name="log_progress_dialog_text">Loading data...</string>
 +
        <string name="saml_authentication_required_text">Authentication required</string>
        <string name="saml_authentication_wrong_pass">Wrong password</string>
        <string name="actionbar_move">Move</string>
@@@ -20,7 -20,7 +20,7 @@@ package com.owncloud.android.datamodel
  
  import java.io.File;
  
 -import com.owncloud.android.utils.Log_OC;
 +import com.owncloud.android.lib.common.utils.Log_OC;
  
  
  import android.os.Parcel;
@@@ -68,8 -68,6 +68,8 @@@ public class OCFile implements Parcelab
      private String mPermissions;
      private String mRemoteId;
  
 +    private boolean mNeedsUpdateThumbnail;
 +
  
      /**
       * Create new {@link OCFile} with given path.
          mPublicLink = source.readString();
          mPermissions = source.readString();
          mRemoteId = source.readString();
 +        mNeedsUpdateThumbnail = source.readInt() == 0;
 +
      }
  
      @Override
          dest.writeString(mPublicLink);
          dest.writeString(mPermissions);
          dest.writeString(mRemoteId);
 +        dest.writeInt(mNeedsUpdateThumbnail ? 1 : 0);
      }
      
      /**
          mPublicLink = null;
          mPermissions = null;
          mRemoteId = null;
 +        mNeedsUpdateThumbnail = false;
      }
  
      /**
          return mNeedsUpdating;
      }
      
 +    public boolean needsUpdateThumbnail() {
 +        return mNeedsUpdateThumbnail;
 +    }
 +
 +    public void setNeedsUpdateThumbnail(boolean needsUpdateThumbnail) {
 +        this.mNeedsUpdateThumbnail = needsUpdateThumbnail;
 +    }
 +
      public long getLastSyncDateForProperties() {
          return mLastSyncDateForProperties;
      }
          } else if (another.isFolder()) {
              return 1;
          }
-         return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
+         return new AlphanumComparator().compare(this, another);
      }
  
      @Override
@@@ -21,6 -21,8 +21,6 @@@ package com.owncloud.android.ui.activit
  import java.io.File;
  import java.io.IOException;
  
 -import org.apache.commons.httpclient.methods.PostMethod;
 -
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.accounts.AuthenticatorException;
@@@ -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;
@@@ -61,7 -64,6 +60,7 @@@ import com.actionbarsherlock.view.Menu
  import com.actionbarsherlock.view.MenuInflater;
  import com.actionbarsherlock.view.MenuItem;
  import com.actionbarsherlock.view.Window;
 +import com.owncloud.android.BuildConfig;
  import com.owncloud.android.MainApp;
  import com.owncloud.android.R;
  import com.owncloud.android.datamodel.OCFile;
@@@ -78,7 -80,6 +77,7 @@@ import com.owncloud.android.lib.common.
  import com.owncloud.android.lib.common.operations.RemoteOperation;
  import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 +import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.operations.CreateFolderOperation;
  import com.owncloud.android.operations.CreateShareOperation;
  import com.owncloud.android.operations.MoveFileOperation;
@@@ -101,6 -102,7 +100,6 @@@ import com.owncloud.android.ui.preview.
  import com.owncloud.android.ui.preview.PreviewVideoActivity;
  import com.owncloud.android.utils.DisplayUtils;
  import com.owncloud.android.utils.ErrorMessageAdapter;
 -import com.owncloud.android.utils.Log_OC;
  
  
  /**
  
  public class FileDisplayActivity extends HookActivity implements
  FileFragment.ContainerActivity, OnNavigationListener, 
 -OnSslUntrustedCertListener, SwipeRefreshLayout.OnRefreshListener {
 +OnSslUntrustedCertListener, OnEnforceableRefreshListener {
      
      private ArrayAdapter<String> mDirectories;
  
      private String DIALOG_UNTRUSTED_CERT;
      
      private OCFile mWaitingToSend;
+     
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          Log_OC.d(TAG, "onCreate() start");
                  Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
                  initFragmentsWithFile();
                  if (file.isFolder()) {
 -                    startSyncFolderOperation(file);
 +                    startSyncFolderOperation(file, false);
                  }
                  
              } else {
              if (listOfFiles != null) {
                  listOfFiles.listDirectory(getCurrentDir());   
              } else {
 -                Log.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
 +                Log_OC.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
              }
              
              /// Second fragment
              }
  
          } else {
 -            Log.wtf(TAG, "initFragments() called with invalid NULLs!");
 +            Log_OC.wtf(TAG, "initFragments() called with invalid NULLs!");
              if (getAccount() == null) {
 -                Log.wtf(TAG, "\t account is NULL");
 +                Log_OC.wtf(TAG, "\t account is NULL");
              }
              if (getFile() == null) {
 -                Log.wtf(TAG, "\t file is NULL");
 +                Log_OC.wtf(TAG, "\t file is NULL");
              }
          }
      }
      }
  
      @Override
 +    public boolean onPrepareOptionsMenu(Menu menu) {
 +        if (BuildConfig.DEBUG) {
 +            menu.findItem(R.id.action_logger).setVisible(true);
 +        } else {
 +            menu.findItem(R.id.action_logger).setVisible(false);
 +        }
 +        return super.onPrepareOptionsMenu(menu);
 +    }
 +
 +    @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          MenuInflater inflater = getSherlock().getMenuInflater();
          inflater.inflate(R.menu.main_menu, menu);
              startActivity(settingsIntent);
              break;
          }
 +        case R.id.action_logger: {
 +            Intent loggerIntent = new Intent(getApplicationContext(),LogHistoryActivity.class);
 +            startActivity(loggerIntent);
 +            break;
 +        }
          case android.R.id.home: {
              FileFragment second = getSecondFragment();
              OCFile currentDir = getCurrentDir();
              }
              break;
          }
+         case R.id.action_sort: {
+             SharedPreferences appPreferences = PreferenceManager
+                     .getDefaultSharedPreferences(getApplicationContext());
+             
+             // Read sorting order, default to sort by name ascending
+             Integer sortOrder = appPreferences
+                     .getInt("sortOrder", 0);
+             
+             AlertDialog.Builder builder = new AlertDialog.Builder(this);
+             builder.setTitle(R.string.actionbar_sort_title)
+             .setSingleChoiceItems(R.array.actionbar_sortby, sortOrder , new DialogInterface.OnClickListener() {
+                 public void onClick(DialogInterface dialog, int which) {
+                     
+                     switch (which){
+                     case 0:
+                         sortByName(true);
+                         break;
+                     case 1:
+                         sortByDate(false);
+                         break;
+                     case 2:
+                         sortBySize(false);
+                         break;
+                     }
+                     
+                     dialog.dismiss();
+                     
+                 }
+             });
+             builder.create().show();
+             break;
+         }
          default:
              retval = super.onOptionsItemSelected(item);
          }
              }
              ocFileListFragment.setMessageForEmptyList(getString(message));
          } else {
 -            Log.e(TAG, "OCFileListFragment is null");
 +            Log_OC.e(TAG, "OCFileListFragment is null");
          }
      }
  
              OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
              listOfFiles.listDirectory(root);
              setFile(listOfFiles.getCurrentFile());
 -            startSyncFolderOperation(root);
 +            startSyncFolderOperation(root, false);
          }
          cleanSecondFragment();
      }
              setNavigationListWithFolder(folder);
              listOfFiles.listDirectory(folder);
              setFile(listOfFiles.getCurrentFile());
 -            startSyncFolderOperation(folder);
 +            startSyncFolderOperation(folder, false);
          } else {
              Log_OC.e(TAG, "Unexpected null when accessing list fragment");
          }
          cleanSecondFragment();
          
          // Sync Folder
 -        startSyncFolderOperation(directory);
 +        startSyncFolderOperation(directory, false);
          
      }
  
  
      @Override
      public void onSavedCertificate() {
 -        startSyncFolderOperation(getCurrentDir());                
 +        startSyncFolderOperation(getCurrentDir(), false);
      }
  
  
          return null;
      }
      
 -    public void startSyncFolderOperation(OCFile folder) {
 +    public void startSyncFolderOperation(OCFile folder, boolean ignoreETag) {
          long currentSyncTime = System.currentTimeMillis(); 
          
          mSyncInProgress = true;
                                                                          currentSyncTime, 
                                                                          false,
                                                                          getFileOperationsHelper().isSharedSupported(),
 +                                                                        ignoreETag,
                                                                          getStorageManager(), 
                                                                          getAccount(), 
                                                                          getApplicationContext()
      }
  
      @Override
 +    public void onRefresh(boolean ignoreETag) {
 +        refreshList(ignoreETag);
 +    }
 +
 +    @Override
      public void onRefresh() {
 +        refreshList(true);
 +    }
 +
 +    private void refreshList(boolean ignoreETag) {
          OCFileListFragment listOfFiles = getListOfFilesFragment();
          if (listOfFiles != null) {
              OCFile folder = listOfFiles.getCurrentFile();
              if (folder != null) {
                  /*mFile = mContainerActivity.getStorageManager().getFileById(mFile.getFileId());
                  listDirectory(mFile);*/
 -                startSyncFolderOperation(folder);
 +                startSyncFolderOperation(folder, ignoreETag);
              }
          }
      }
 -    
++
+     private void sortByDate(boolean ascending){
+         getListOfFilesFragment().sortByDate(ascending);
+     }
+     private void sortBySize(boolean ascending){
+         getListOfFilesFragment().sortBySize(ascending);
+     }
+     private void sortByName(boolean ascending){
+         getListOfFilesFragment().sortByName(ascending);
+     }
 -
  }
  package com.owncloud.android.ui.adapter;\r
  \r
  import java.io.File;\r
 +import java.lang.ref.WeakReference;\r
+ import java.util.Collections;\r
+ import java.util.Comparator;\r
  import java.util.Vector;\r
  \r
  import android.accounts.Account;\r
  import android.content.Context;\r
+ import android.content.SharedPreferences;\r
 +import android.content.res.Resources;\r
 +import android.graphics.Bitmap;\r
 +import android.graphics.Bitmap.CompressFormat;\r
 +import android.graphics.BitmapFactory;\r
 +import android.graphics.drawable.BitmapDrawable;\r
 +import android.graphics.drawable.Drawable;\r
 +import android.media.ThumbnailUtils;\r
 +import android.os.AsyncTask;\r
+ import android.preference.PreferenceManager;\r
 +import android.util.TypedValue;\r
  import android.view.LayoutInflater;\r
  import android.view.View;\r
  import android.view.ViewGroup;\r
@@@ -43,14 -37,15 +47,17 @@@ import android.widget.TextView
  \r
  import com.owncloud.android.R;\r
  import com.owncloud.android.authentication.AccountUtils;\r
+ import com.owncloud.android.datamodel.AlphanumComparator;\r
  import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.datamodel.OCFile;\r
  import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
 +import com.owncloud.android.lib.common.utils.Log_OC;\r
  import com.owncloud.android.ui.activity.ComponentsGetter;\r
 +import com.owncloud.android.utils.BitmapUtils;\r
  import com.owncloud.android.utils.DisplayUtils;\r
 -import com.owncloud.android.utils.Log_OC;\r
+ import com.owncloud.android.utils.FileStorageUtils;\r
++\r
  \r
  \r
  /**\r
   * instance.\r
   * \r
   * @author Bartek Przybylski\r
 - * \r
 + * @author Tobias Kaminsky\r
 + * @author David A. Velasco\r
   */\r
  public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
      private final static String PERMISSION_SHARED_WITH_ME = "S";\r
 +    \r
 +    private static final String TAG = FileListListAdapter.class.getSimpleName();\r
  \r
      private Context mContext;\r
      private OCFile mFile = null;\r
      private FileDataStorageManager mStorageManager;
      private Account mAccount;
      private ComponentsGetter mTransferServiceGetter;\r
+     private Integer sortOrder;\r
+     private Boolean sortAscending;\r
+     private SharedPreferences appPreferences;\r
      \r
 +    private final Object thumbnailDiskCacheLock = new Object();\r
 +    private DiskLruImageCache mThumbnailCache;\r
 +    private boolean mThumbnailCacheStarting = true;\r
 +    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB\r
 +    private static final CompressFormat mCompressFormat = CompressFormat.JPEG;\r
 +    private static final int mCompressQuality = 70;\r
 +    private Bitmap defaultImg;\r
 +        \r
      public FileListListAdapter(\r
              boolean justFolders, \r
              Context context, \r
              ComponentsGetter transferServiceGetter\r
              ) {\r
 +        \r
          mJustFolders = justFolders;\r
          mContext = context;\r
          mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
          mTransferServiceGetter = transferServiceGetter;\r
-         return null;\r
+         \r
+         appPreferences = PreferenceManager\r
+                 .getDefaultSharedPreferences(mContext);\r
+         \r
+         // Read sorting order, default to sort by name ascending\r
+         sortOrder = appPreferences\r
+                 .getInt("sortOrder", 0);\r
+         sortAscending = appPreferences.getBoolean("sortAscending", true);\r
+         \r
 +        defaultImg = BitmapFactory.decodeResource(mContext.getResources(), \r
 +                    DisplayUtils.getResourceId("image/png", "default.png"));\r
 +        \r
 +        // Initialise disk cache on background thread\r
 +        new InitDiskCacheTask().execute();\r
 +    }\r
 +    \r
 +    class InitDiskCacheTask extends AsyncTask<File, Void, Void> {\r
 +        @Override\r
 +        protected Void doInBackground(File... params) {\r
 +            synchronized (thumbnailDiskCacheLock) {\r
 +                try {\r
 +                    mThumbnailCache = new DiskLruImageCache(mContext, "thumbnailCache", \r
 +                                        DISK_CACHE_SIZE, mCompressFormat, mCompressQuality);\r
 +                } catch (Exception e) {\r
 +                    Log_OC.d(TAG, "Thumbnail cache could not be opened ", e);\r
 +                    mThumbnailCache = null;\r
 +                }\r
 +                mThumbnailCacheStarting = false; // Finished initialization\r
 +                thumbnailDiskCacheLock.notifyAll(); // Wake any waiting threads\r
 +            }\r
 +            return null;\r
 +        }\r
 +    }\r
 +    \r
 +    static class AsyncDrawable extends BitmapDrawable {\r
 +        private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference;\r
 +\r
 +        public AsyncDrawable(Resources res, Bitmap bitmap,\r
 +                ThumbnailGenerationTask bitmapWorkerTask) {\r
 +            super(res, bitmap);\r
 +            bitmapWorkerTaskReference =\r
 +                new WeakReference<ThumbnailGenerationTask>(bitmapWorkerTask);\r
 +        }\r
 +\r
 +        public ThumbnailGenerationTask getBitmapWorkerTask() {\r
 +            return bitmapWorkerTaskReference.get();\r
 +        }\r
 +    }\r
 +\r
 +    class ThumbnailGenerationTask extends AsyncTask<OCFile, Void, Bitmap> {\r
 +        private final WeakReference<ImageView> imageViewReference;\r
 +        private OCFile file;\r
 +\r
 +        \r
 +        public ThumbnailGenerationTask(ImageView imageView) {\r
 +         // Use a WeakReference to ensure the ImageView can be garbage collected\r
 +            imageViewReference = new WeakReference<ImageView>(imageView);\r
 +        }\r
 +\r
 +        // Decode image in background.\r
 +        @Override\r
 +        protected Bitmap doInBackground(OCFile... params) {\r
 +            Bitmap thumbnail = null;\r
 +            \r
 +            try {\r
 +                file = params[0];\r
 +                final String imageKey = String.valueOf(file.getRemoteId());\r
 +    \r
 +                // Check disk cache in background thread\r
 +                thumbnail = getBitmapFromDiskCache(imageKey);\r
 +    \r
 +                // Not found in disk cache\r
 +                if (thumbnail == null || file.needsUpdateThumbnail()) { \r
 +                    // Converts dp to pixel\r
 +                    Resources r = mContext.getResources();\r
 +                    int px = (int) Math.round(TypedValue.applyDimension(\r
 +                            TypedValue.COMPLEX_UNIT_DIP, 150, r.getDisplayMetrics()\r
 +                    ));\r
 +                    \r
 +                    if (file.isDown()){\r
 +                        Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(\r
 +                                file.getStoragePath(), px, px);\r
 +                        \r
 +                        if (bitmap != null) {\r
 +                            thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);\r
 +    \r
 +                            // Add thumbnail to cache\r
 +                            addBitmapToCache(imageKey, thumbnail);\r
 +\r
 +                            file.setNeedsUpdateThumbnail(false);\r
 +                            mStorageManager.saveFile(file);\r
 +                        }\r
 +    \r
 +                    }\r
 +                }\r
 +                \r
 +            } catch (Throwable t) {\r
 +                // the app should never break due to a problem with thumbnails\r
 +                Log_OC.e(TAG, "Generation of thumbnail for " + file + " failed", t);\r
 +                if (t instanceof OutOfMemoryError) {\r
 +                    System.gc();\r
 +                }\r
 +            }\r
 +            \r
 +            return thumbnail;\r
 +        }\r
 +        \r
 +        protected void onPostExecute(Bitmap bitmap){\r
 +            if (isCancelled()) {\r
 +                bitmap = null;\r
 +            }\r
 +\r
 +            if (imageViewReference != null && bitmap != null) {\r
 +                final ImageView imageView = imageViewReference.get();\r
 +                final ThumbnailGenerationTask bitmapWorkerTask =\r
 +                        getBitmapWorkerTask(imageView);\r
 +                if (this == bitmapWorkerTask && imageView != null) {\r
 +                    imageView.setImageBitmap(bitmap);\r
 +                }\r
 +            }\r
 +        }\r
 +    }\r
 +  \r
 +    public void addBitmapToCache(String key, Bitmap bitmap) {\r
 +        synchronized (thumbnailDiskCacheLock) {\r
 +            if (mThumbnailCache != null) {\r
 +                mThumbnailCache.put(key, bitmap);\r
 +            }\r
 +        }\r
 +    }\r
 +\r
 +    public Bitmap getBitmapFromDiskCache(String key) {\r
 +        synchronized (thumbnailDiskCacheLock) {\r
 +            // Wait while disk cache is started from background thread\r
 +            while (mThumbnailCacheStarting) {\r
 +                try {\r
 +                    thumbnailDiskCacheLock.wait();\r
 +                } catch (InterruptedException e) {}\r
 +            }\r
 +            if (mThumbnailCache != null) {\r
 +                return (Bitmap) mThumbnailCache.getBitmap(key);\r
 +            }\r
 +        }\r
++        return null;
      }
  \r
      @Override\r
                      .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
              view = inflator.inflate(R.layout.list_item, null);\r
          }\r
 -    \r
 +         \r
          if (mFiles != null && mFiles.size() > position) {\r
              OCFile file = mFiles.get(position);\r
 -            TextView fileName = (TextView) view.findViewById(R.id.Filename);\r
 +            TextView fileName = (TextView) view.findViewById(R.id.Filename);           \r
              String name = file.getFileName();\r
  \r
              fileName.setText(name);\r
  \r
              ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
              localStateView.bringToFront();\r
 -            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
 +            FileDownloaderBinder downloaderBinder = \r
 +                    mTransferServiceGetter.getFileDownloaderBinder();\r
              FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
              if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
                  localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
                  fileSizeV.setVisibility(View.VISIBLE);\r
                  fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
                  lastModV.setVisibility(View.VISIBLE);\r
 -                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
 +                lastModV.setText(\r
 +                        DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())\r
 +                );\r
                  // this if-else is needed even thoe fav icon is visible by default\r
                  // because android reuses views in listview\r
                  if (!file.keepInSync()) {\r
                          checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
                      }\r
                      checkBoxV.setVisibility(View.VISIBLE);\r
 +                }               \r
 +                \r
 +                // get Thumbnail if file is image\r
 +                if (file.isImage()){\r
 +                     // Thumbnail in Cache?\r
 +                    Bitmap thumbnail = getBitmapFromDiskCache(String.valueOf(file.getRemoteId()));\r
 +                    if (thumbnail != null && !file.needsUpdateThumbnail()){\r
 +                        fileIcon.setImageBitmap(thumbnail);\r
 +                    } else {\r
 +                        // generate new Thumbnail\r
 +                        if (cancelPotentialWork(file, fileIcon)) {\r
 +                            final ThumbnailGenerationTask task = \r
 +                                    new ThumbnailGenerationTask(fileIcon);\r
 +                            final AsyncDrawable asyncDrawable =\r
 +                                    new AsyncDrawable(mContext.getResources(), defaultImg, task);\r
 +                            fileIcon.setImageDrawable(asyncDrawable);\r
 +                            task.execute(file);\r
 +                        }\r
 +                    }\r
 +                } else {\r
 +                    fileIcon.setImageResource(\r
 +                            DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())\r
 +                    );\r
                  }\r
 -\r
 -                fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));\r
 -\r
 +
                  if (checkIfFileIsSharedWithMe(file)) {\r
                      sharedWithMeIconV.setVisibility(View.VISIBLE);\r
                  }\r
              } \r
              else {\r
-                 fileSizeV.setVisibility(View.INVISIBLE);\r
-                 //fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+                 if (FileStorageUtils.getDefaultSavePathFor(mAccount.name, file) != null){\r
+                     fileSizeV.setVisibility(View.VISIBLE);\r
+                     fileSizeV.setText(getFolderSizeHuman(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)));\r
+                 } else {\r
+                     fileSizeV.setVisibility(View.INVISIBLE);\r
+                 }\r
 -                \r
++
                  lastModV.setVisibility(View.VISIBLE);\r
 -                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
 +                lastModV.setText(\r
 +                        DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())\r
 +                );\r
                  checkBoxV.setVisibility(View.GONE);\r
                  view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
  \r
                      fileIcon.setImageResource(R.drawable.shared_with_me_folder);\r
                      sharedWithMeIconV.setVisibility(View.VISIBLE);\r
                  } else {\r
 -                    fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));\r
 +                    fileIcon.setImageResource(\r
 +                            DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())\r
 +                    );\r
                  }\r
  \r
                  // If folder is sharedByLink, icon folder must be changed to\r
  \r
          return view;\r
      }\r
-     \r
++
 +    public static boolean cancelPotentialWork(OCFile file, ImageView imageView) {\r
 +        final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView);\r
 +\r
 +        if (bitmapWorkerTask != null) {\r
 +            final OCFile bitmapData = bitmapWorkerTask.file;\r
 +            // If bitmapData is not yet set or it differs from the new data\r
 +            if (bitmapData == null || bitmapData != file) {\r
 +                // Cancel previous task\r
 +                bitmapWorkerTask.cancel(true);\r
 +            } else {\r
 +                // The same work is already in progress\r
 +                return false;\r
 +            }\r
 +        }\r
 +        // No task associated with the ImageView, or an existing task was cancelled\r
 +        return true;\r
 +    }\r
      \r
 -    } \r
 +    private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) {\r
 +        if (imageView != null) {\r
 +            final Drawable drawable = imageView.getDrawable();\r
 +            if (drawable instanceof AsyncDrawable) {\r
 +                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;\r
 +                return asyncDrawable.getBitmapWorkerTask();\r
 +            }\r
 +         }\r
 +         return null;\r
 +     }\r
++
+     /**\r
+      * Local Folder size in human readable format\r
+      * @param path String\r
+      * @return Size in human readable format\r
+      */\r
+     private String getFolderSizeHuman(String path) {\r
\r
+         File dir = new File(path);\r
\r
+         if(dir.exists()) {\r
+             long bytes = getFolderSize(dir);\r
+             if (bytes < 1024) return bytes + " B";\r
+             int exp = (int) (Math.log(bytes) / Math.log(1024));\r
+             String pre = ("KMGTPE").charAt(exp-1) + "";\r
\r
+             return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);\r
+         }\r
\r
+         return "0 B";\r
+     }\r
\r
+     /**\r
+      * Local Folder size\r
+      * @param dir File\r
+      * @return Size in bytes\r
+      */\r
+     private long getFolderSize(File dir) {\r
+         if (dir.exists()) {\r
+             long result = 0;\r
+             File[] fileList = dir.listFiles();\r
+             for(int i = 0; i < fileList.length; i++) {\r
+                 if(fileList[i].isDirectory()) {\r
+                     result += getFolderSize(fileList[i]);\r
+                 } else {\r
+                     result += fileList[i].length();\r
+                 }\r
+             }\r
+             return result;\r
+         }\r
+         return 0;\r
++    } 
  \r
      @Override\r
      public int getViewTypeCount() {\r
  \r
      /**\r
       * Change the adapted directory for a new one\r
 -     * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".\r
 -     * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
 +     * @param directory                 New file to adapt. Can be NULL, meaning \r
 +     *                                  "no content to adapt".\r
 +     * @param updatedStorageManager     Optional updated storage manager; used to replace \r
 +     *                                  mStorageManager if is different (and not NULL)\r
       */\r
      public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {\r
          mFile = directory;\r
          } else {\r
              mFiles = null;\r
          }\r
\r
+         sortDirectory();\r
+     }\r
+     \r
+     /**\r
+      * Sorts all filenames, regarding last user decision \r
+      */\r
+     private void sortDirectory(){\r
+         switch (sortOrder){\r
+         case 0:\r
+             sortByName(sortAscending);\r
+             break;\r
+         case 1:\r
+             sortByDate(sortAscending);\r
+             break;\r
+         case 2: \r
+             sortBySize(sortAscending);\r
+             break;\r
+         }\r
+         \r
          notifyDataSetChanged();\r
      }\r
      \r
       * @return boolean: True if it is shared with me and false if it is not\r
       */\r
      private boolean checkIfFileIsSharedWithMe(OCFile file) {\r
 -        return (mFile.getPermissions() != null && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)\r
 -                && file.getPermissions() != null && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));\r
 +        return (mFile.getPermissions() != null \r
 +                && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)\r
 +                && file.getPermissions() != null \r
 +                && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));\r
      }\r
\r
+     /**\r
+      * Sorts list by Date\r
+      * @param sortAscending true: ascending, false: descending\r
+      */\r
+     private void sortByDate(boolean sortAscending){\r
+         final Integer val;\r
+         if (sortAscending){\r
+             val = 1;\r
+         } else {\r
+             val = -1;\r
+         }\r
+         \r
+         Collections.sort(mFiles, new Comparator<OCFile>() {\r
+             public int compare(OCFile o1, OCFile o2) {\r
+                 if (o1.isFolder() && o2.isFolder()) {\r
+                     return val * Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp());\r
+                 }\r
+                 else if (o1.isFolder()) {\r
+                     return -1;\r
+                 } else if (o2.isFolder()) {\r
+                     return 1;\r
+                 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){\r
+                     return 0;\r
+                 } else {\r
+                     return val * Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp());\r
+                 }\r
+             }\r
+         });\r
+     }\r
\r
+     /**\r
+      * Sorts list by Size\r
+      * @param sortAscending true: ascending, false: descending\r
+      */\r
+     private void sortBySize(boolean sortAscending){\r
+         final Integer val;\r
+         if (sortAscending){\r
+             val = 1;\r
+         } else {\r
+             val = -1;\r
+         }\r
+         \r
+         Collections.sort(mFiles, new Comparator<OCFile>() {\r
+             public int compare(OCFile o1, OCFile o2) {\r
+                 if (o1.isFolder() && o2.isFolder()) {\r
+                     return val * Long.compare(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1))), \r
+                                               getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));\r
+                 }\r
+                 else if (o1.isFolder()) {\r
+                     return -1;\r
+                 } else if (o2.isFolder()) {\r
+                     return 1;\r
+                 } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){\r
+                     return 0;\r
+                 } else {\r
+                     return val * Long.compare(o1.getFileLength(), o2.getFileLength());\r
+                 }\r
+             }\r
+         });\r
+     }\r
\r
+     /**\r
+      * Sorts list by Name\r
+      * @param sortAscending true: ascending, false: descending\r
+      */\r
+     private void sortByName(boolean sortAscending){\r
+         final Integer val;\r
+         if (sortAscending){\r
+             val = 1;\r
+         } else {\r
+             val = -1;\r
+         }\r
\r
+         Collections.sort(mFiles, new Comparator<OCFile>() {\r
+             public int compare(OCFile o1, OCFile o2) {\r
+                 if (o1.isFolder() && o2.isFolder()) {\r
+                     return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase());\r
+                 } else if (o1.isFolder()) {\r
+                     return -1;\r
+                 } else if (o2.isFolder()) {\r
+                     return 1;\r
+                 }\r
+                 return val * new AlphanumComparator().compare(o1, o2);\r
+             }\r
+         });\r
+     }\r
\r
+     public void setSortOrder(Integer order, boolean ascending) {\r
+         SharedPreferences.Editor editor = appPreferences.edit();\r
+         editor.putInt("sortOrder", order);\r
+         editor.putBoolean("sortAscending", ascending);\r
+         editor.commit();\r
+         \r
+         sortOrder = order;\r
+         sortAscending = ascending;\r
+         \r
+         sortDirectory();\r
+     }    \r
  }\r
@@@ -34,16 -34,15 +34,16 @@@ import com.owncloud.android.R
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.FileMenuFilter;
 +import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
  import com.owncloud.android.ui.activity.MoveActivity;
 +import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
  import com.owncloud.android.ui.adapter.FileListListAdapter;
  import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
  import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
  import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  import com.owncloud.android.ui.preview.PreviewMediaFragment;
 -import com.owncloud.android.utils.Log_OC;
  
  /**
   * A Fragment that lists all files and folders in a given path.
@@@ -89,7 -88,7 +89,7 @@@ public class OCFileListFragment extend
                      FileFragment.ContainerActivity.class.getSimpleName());
          }
          try {
 -            setOnRefreshListener((SwipeRefreshLayout.OnRefreshListener) activity);
 +            setOnRefreshListener((OnEnforceableRefreshListener) activity);
              
          } catch (ClassCastException e) {
              throw new ClassCastException(activity.toString() + " must implement " + 
              
              listDirectory(mFile);
  
 -            onRefresh();
 +            onRefresh(false);
              
              // restore index and top position
              restoreIndexAndTopPosition();
              mFile = directory;
          }
      }
+     
+     public void sortByName(boolean descending){
+         mAdapter.setSortOrder(0, descending);
+     } 
+     
+     public void sortByDate(boolean descending){
+         mAdapter.setSortOrder(1, descending);
+     }
  
+     public void sortBySize(boolean descending){
+         mAdapter.setSortOrder(2, descending);
+     }    
  }