Merge remote-tracking branch 'remotes/upstream/video_thumbnail' into beta
authortobiasKaminsky <tobias@kaminsky.me>
Sun, 1 Nov 2015 08:14:09 +0000 (09:14 +0100)
committertobiasKaminsky <tobias@kaminsky.me>
Sun, 1 Nov 2015 08:14:09 +0000 (09:14 +0100)
1  2 
CHANGELOG.md
src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

diff --combined CHANGELOG.md
@@@ -1,26 -1,42 +1,30 @@@
 -## 1.7.2 (July 2015)
 -- New navigation drawer
 -- Improved Passcode
 -- Automatic grid view just for folders full of images
 -- More characters allowed in file names
 -- Support for servers in same domain, different path
 -- Bugs fixed:
 -  + Frequent crashes in folder with several images
 -  + Sync error in servers with huge quota and external storage enable
 -  + Share by link error 
 -  + Some other crashes and minor bugs
++# 2015-11-01
++- PR [#1236](https://github.com/owncloud/android/pull/1236) "Streaming video/audio" merged
++- PR [#1035](https://github.com/owncloud/android/pull/1035) "Enable video thumbnail" merged
 -## 1.7.1 (April 2015)
 +# 2015-10-31
 +- updated all PR
 +- bugfix: #1234, #1230
 +- implement Crash Handler
 +- implement direct download of latest apk in settings -> last item on bottom
  
 -- Share link even with password enforced by server
 -- Get the app ready for oc 8.1 servers
 -- Added option to create new folder in uploads from external apps
 -- Improved management of deleted users
 -- Bugs fixed
 -  + Fixed crash on Android 2.x devices
 -  + Improvements on uploads
 -
 -## 1.7.0 (February 2015)
 -
 -- Download full folders
 -- Grid view for images
 -- Remote thumbnails (OC Server 8.0+)
 -- Added number of files and folders at the end of the list
 -- "Open with" in contextual menu
 -- Downloads added to Media Provider
 -- Uploads:
 -  + Local thumbnails in section "Files"
 -  + Multiple selection in "Content from other apps" (Android 4.3+)
 -- Gallery: 
 -  + proper handling of EXIF
 -  + obey sorting in the list of files
 -- Settings view updated
 -- Improved subjects in e-mails
 -- Bugs fixed
 +# 2015-10-30
 +- fixed problem with Authority
  
 +# 2015-10-29
 +- PR [#1099](https://github.com/owncloud/android/pull/1099) "Switch list vs grid" merged
 +- PR [#1100](https://github.com/owncloud/android/pull/1100) "Material FAB with speed dial implementation" merged
 +- PR [#1209](https://github.com/owncloud/android/pull/1209) "Material buttons - before in #1090" merged
 +- PR [#1205](https://github.com/owncloud/android/pull/1205) "Switch between online and offline files" merged
 +- PR [#1195](https://github.com/owncloud/android/pull/1195) "Resize Cache" merged
 +- PR [#1187](https://github.com/owncloud/android/pull/1187) "Video: Big thumbnails" merged
 +- PR [#1058](https://github.com/owncloud/android/pull/1058) "add sort to UploadFileActiviy" merged
 +- PR [#1168](https://github.com/owncloud/android/pull/1168) "Avoid duplicate files" merged
 +- PR [#1176](https://github.com/owncloud/android/pull/1176) "Multi select" merged
  
  
 +# 2015-10-26
 +- start of branch
 +- PR [#745](https://github.com/owncloud/android/pull/745) merged
 +- PR [#1044](https://github.com/owncloud/android/pull/1044) merged: < 8.1: GalleryPlus app needed, >= 8.2 Gallery app needed
 +- PR [#1111](https://github.com/owncloud/android/pull/1111) merged
@@@ -24,30 -24,25 +24,34 @@@ package com.owncloud.android.datamodel
  import java.io.File;
  import java.io.InputStream;
  import java.lang.ref.WeakReference;
+ import java.net.FileNameMap;
+ import java.net.URLConnection;
  
  import org.apache.commons.httpclient.HttpStatus;
  import org.apache.commons.httpclient.methods.GetMethod;
  
  import android.accounts.Account;
 +import android.accounts.AccountManager;
 +import android.content.Context;
  import android.content.res.Resources;
  import android.graphics.Bitmap;
  import android.graphics.Bitmap.CompressFormat;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
 +import android.graphics.Point;
++import android.graphics.Canvas;
+ import android.graphics.Paint;
  import android.graphics.drawable.BitmapDrawable;
 +import android.graphics.drawable.ColorDrawable;
  import android.graphics.drawable.Drawable;
  import android.media.ThumbnailUtils;
  import android.net.Uri;
  import android.os.AsyncTask;
 +import android.view.Display;
 +import android.view.View;
 +import android.view.WindowManager;
  import android.widget.ImageView;
 +import android.widget.ProgressBar;
  
  import com.owncloud.android.MainApp;
  import com.owncloud.android.R;
@@@ -60,6 -55,7 +64,7 @@@ import com.owncloud.android.lib.resourc
  import com.owncloud.android.ui.adapter.DiskLruImageCache;
  import com.owncloud.android.utils.BitmapUtils;
  import com.owncloud.android.utils.DisplayUtils;
+ import com.owncloud.android.utils.FileStorageUtils;
  
  /**
   * Manager for concurrent access to thumbnails cache.
@@@ -81,8 -77,8 +86,8 @@@ public class ThumbnailsCacheManager 
  
      public static Bitmap mDefaultImg = 
              BitmapFactory.decodeResource(
 -                    MainApp.getAppContext().getResources(), 
 -                    DisplayUtils.getFileTypeIconId("image/png", "default.png")
 +                    MainApp.getAppContext().getResources(),
 +                    R.drawable.file_image
              );
  
      
          return null;
      }
  
 +    /**
 +     * Sets max size of cache
 +     * @param maxSize in MB
 +     * @return
 +     */
 +    public static boolean setMaxSize(long maxSize){
 +        if (mThumbnailCache != null){
 +            mThumbnailCache.setMaxSize(maxSize * 1024 * 1024);
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    public static long getMaxSize(){
 +        if (mThumbnailCache != null) {
 +            return mThumbnailCache.getMaxSize();
 +        } else {
 +            return -1l;
 +        }
 +    }
 +
      public static class ThumbnailGenerationTask extends AsyncTask<Object, Void, Bitmap> {
          private final WeakReference<ImageView> mImageViewReference;
 +        private WeakReference<ProgressBar> mProgressWheelRef;
          private static Account mAccount;
          private Object mFile;
 +        private Boolean mIsThumbnail;
          private FileDataStorageManager mStorageManager;
  
 -
          public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager,
                                         Account account) {
              // Use a WeakReference to ensure the ImageView can be garbage collected
              mAccount = account;
          }
  
 +        public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager,
 +                                       Account account, ProgressBar progressWheel) {
 +        this(imageView, storageManager, account);
 +        mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
 +        }
 +
          public ThumbnailGenerationTask(ImageView imageView) {
              // Use a WeakReference to ensure the ImageView can be garbage collected
              mImageViewReference = new WeakReference<ImageView>(imageView);
                  }
  
                  mFile = params[0];
 +                mIsThumbnail = (Boolean) params[1];
 +
                  
                  if (mFile instanceof OCFile) {
 -                    thumbnail = doOCFileInBackground();
 +                    thumbnail = doOCFileInBackground(mIsThumbnail);
+                     if (((OCFile) mFile).isVideo()){
+                         thumbnail = addVideoOverlay(thumbnail);
+                     }
                  }  else if (mFile instanceof File) {
 -                    thumbnail = doFileInBackground();
 +                    thumbnail = doFileInBackground(mIsThumbnail);
-                 } else {
-                     // do nothing
+                     String url = ((File) mFile).getAbsolutePath();
+                     String mMimeType = FileStorageUtils.getMimeTypeFromName(url);
+                     if (mMimeType != null && mMimeType.startsWith("video/")){
+                         thumbnail = addVideoOverlay(thumbnail);
+                     }
+                 //} else {  do nothing
                  }
  
                  }catch(Throwable t){
          }
  
          protected void onPostExecute(Bitmap bitmap){
 -            if (isCancelled()) {
 -                bitmap = null;
 -            }
 -
              if (bitmap != null) {
                  final ImageView imageView = mImageViewReference.get();
                  final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
                          tagId = String.valueOf(mFile.hashCode());
                      }
                      if (String.valueOf(imageView.getTag()).equals(tagId)) {
 +                        if (mProgressWheelRef != null) {
 +                            final ProgressBar progressWheel = mProgressWheelRef.get();
 +                            if (progressWheel != null) {
 +                                progressWheel.setVisibility(View.GONE);
 +                            }
 +                        }
                          imageView.setImageBitmap(bitmap);
 +                        imageView.setVisibility(View.VISIBLE);
                      }
                  }
              }
           * @param imageKey: thumb key
           * @param bitmap:   image for extracting thumbnail
           * @param path:     image path
 -         * @param px:       thumbnail dp
 +         * @param pxW:       thumbnail width
 +         * @param pxH:       thumbnail height
           * @return Bitmap
           */
 -        private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){
 +        private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){
  
 -            Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
 +            Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
  
              // Rotate image, obeying exif tag
              thumbnail = BitmapUtils.rotateImage(thumbnail,path);
              return Math.round(r.getDimension(R.dimen.file_icon_size_grid));
          }
  
 -        private Bitmap doOCFileInBackground() {
 +        private Point getScreenDimension(){
 +            WindowManager wm = (WindowManager) MainApp.getAppContext().getSystemService(Context.WINDOW_SERVICE);
 +            Display display = wm.getDefaultDisplay();
 +            Point test = new Point();
 +            display.getSize(test);
 +            return test;
 +        }
 +
 +        private Bitmap doOCFileInBackground(Boolean isThumbnail) {
 +            Bitmap thumbnail = null;
              OCFile file = (OCFile)mFile;
  
 -            final String imageKey = String.valueOf(file.getRemoteId());
 +            // distinguish between thumbnail and resized image
 +            String temp = String.valueOf(file.getRemoteId());
 +            if (isThumbnail){
 +                temp = "t" + temp;
 +            } else {
 +                temp = "r" + temp;
 +            }
 +
 +            final String imageKey = temp;
  
              // Check disk cache in background thread
 -            Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
 +            thumbnail = getBitmapFromDiskCache(imageKey);
  
              // Not found in disk cache
              if (thumbnail == null || file.needsUpdateThumbnail()) {
 -
 -                int px = getThumbnailDimension();
 +                int pxW = 0;
 +                int pxH = 0;
 +                if (mIsThumbnail) {
 +                    pxW = pxH = getThumbnailDimension();
 +                } else {
 +                    Point p = getScreenDimension();
 +                    pxW = p.x;
 +                    pxH = p.y;
 +                }
  
                  if (file.isDown()) {
 -                    Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
 -                            file.getStoragePath(), px, px);
 +                    Bitmap tempBitmap = BitmapUtils.decodeSampledBitmapFromFile(
 +                            file.getStoragePath(), pxW, pxH);
 +                    Bitmap bitmap = ThumbnailUtils.extractThumbnail(tempBitmap, pxW, pxH);
  
                      if (bitmap != null) {
 -                        thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), px);
 +                        // Handle PNG
 +                        if (file.getMimetype().equalsIgnoreCase("image/png")) {
 +                            bitmap = handlePNG(bitmap, pxW);
 +                        }
 +
 +                        thumbnail = addThumbnailToCache(imageKey, bitmap,
 +                                                        file.getStoragePath(), pxW, pxH);
  
                          file.setNeedsUpdateThumbnail(false);
                          mStorageManager.saveFile(file);
                      if (mClient != null && serverOCVersion != null) {
                          if (serverOCVersion.supportsRemoteThumbnails()) {
                              try {
 -                                String uri = mClient.getBaseUri() + "" +
 -                                        "/index.php/apps/files/api/v1/thumbnail/" +
 -                                        px + "/" + px + Uri.encode(file.getRemotePath(), "/");
 -                                Log_OC.d("Thumbnail", "URI: " + uri);
 -                                GetMethod get = new GetMethod(uri);
 -                                int status = mClient.executeMethod(get);
 -                                if (status == HttpStatus.SC_OK) {
 -//                                    byte[] bytes = get.getResponseBody();
 -//                                    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
 -//                                            bytes.length);
 -                                    InputStream inputStream = get.getResponseBodyAsStream();
 -                                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
 -                                    thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
 -
 -                                    // Add thumbnail to cache
 -                                    if (thumbnail != null) {
 -                                        addBitmapToCache(imageKey, thumbnail);
 +                                if (mIsThumbnail) {
 +                                    String uri = mClient.getBaseUri() + "" +
 +                                            "/index.php/apps/files/api/v1/thumbnail/" +
 +                                            pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/");
 +                                    Log_OC.d("Thumbnail", "Download URI: " + uri);
 +                                    GetMethod get = new GetMethod(uri);
 +                                    int status = mClient.executeMethod(get);
 +                                    if (status == HttpStatus.SC_OK) {
 +                                        InputStream inputStream = get.getResponseBodyAsStream();
 +                                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
 +                                        thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
 +                                    } else {
 +                                        Log_OC.d(TAG, "Status: " + status);
 +                                    }
 +                                } else {
 +                                    String gallery = "";
 +                                    if (serverOCVersion.supportsNativeGallery()){
 +                                        gallery = "gallery";
 +                                    } else {
 +                                        gallery = "galleryplus";
                                      }
 +
 +                                    String uri = mClient.getBaseUri() +
 +                                            "/index.php/apps/" + gallery + "/api/preview/" + Integer.parseInt(file.getRemoteId().substring(0,8)) +
 +                                            "/" + pxW + "/" + pxH;
 +                                    Log_OC.d("Thumbnail", "FileName: " + file.getFileName() + " Download URI: " + uri);
 +                                    GetMethod get = new GetMethod(uri);
 +                                    int status = mClient.executeMethod(get);
 +                                    if (status == HttpStatus.SC_OK) {
 +                                        InputStream inputStream = get.getResponseBodyAsStream();
 +                                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
 +                                        // Download via gallery app
 +                                        thumbnail = bitmap;
 +                                    }
 +                                }
 +
 +                                // Handle PNG
 +                                if (thumbnail != null && file.getMimetype().equalsIgnoreCase("image/png")) {
 +                                    thumbnail = handlePNG(thumbnail, pxW);
 +                                }
 +
 +                                // Add thumbnail to cache
 +                                if (thumbnail != null) {
 +                                    addBitmapToCache(imageKey, thumbnail);
                                  }
                              } catch (Exception e) {
                                  e.printStackTrace();
  
          }
  
 -        private Bitmap doFileInBackground() {
 +        private Bitmap handlePNG(Bitmap bitmap, int px){
 +            Bitmap resultBitmap = Bitmap.createBitmap(px,
 +                    px,
 +                    Bitmap.Config.ARGB_8888);
 +            Canvas c = new Canvas(resultBitmap);
 +
 +            c.drawColor(MainApp.getAppContext().getResources().
 +                    getColor(R.color.background_color));
 +            c.drawBitmap(bitmap, 0, 0, null);
 +
 +            return resultBitmap;
 +        }
 +
 +        private Bitmap doFileInBackground(Boolean mIsThumbnail) {
              File file = (File)mFile;
  
 -            final String imageKey = String.valueOf(file.hashCode());
 +            // distinguish between thumbnail and resized image
 +            String temp = String.valueOf(file.hashCode());
 +            if (mIsThumbnail){
 +                temp = "t" + temp;
 +            } else {
 +                temp = "r" + temp;
 +            }
 +
 +            final String imageKey = temp;
  
              // Check disk cache in background thread
              Bitmap thumbnail = getBitmapFromDiskCache(imageKey);
  
              // Not found in disk cache
              if (thumbnail == null) {
 -
 -                int px = getThumbnailDimension();
 +                int pxW = 0;
 +                int pxH = 0;
 +                if (mIsThumbnail) {
 +                    pxW = pxH = getThumbnailDimension();
 +                } else {
 +                    Point p = getScreenDimension();
 +                    pxW = p.x;
 +                    pxH = p.y;
 +                }
  
                  Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(
 -                        file.getAbsolutePath(), px, px);
 +                        file.getAbsolutePath(), pxW, pxH);
  
                  if (bitmap != null) {
 -                    thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
 +                    thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH);
                  }
              }
              return thumbnail;
              if (bitmapData == null || bitmapData != file) {
                  // Cancel previous task
                  bitmapWorkerTask.cancel(true);
 +                Log_OC.v(TAG, "Cancelled generation of thumbnail for a reused imageView");
              } else {
                  // The same work is already in progress
                  return false;
          return null;
      }
  
+     public static Bitmap addVideoOverlay(Bitmap thumbnail){
+         Bitmap playButton = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(),
+                 R.drawable.view_play);
+         Bitmap resizedPlayButton = Bitmap.createScaledBitmap(playButton,
+                 (int) (thumbnail.getWidth() * 0.3),
+                 (int) (thumbnail.getHeight() * 0.3), true);
+         Bitmap resultBitmap = Bitmap.createBitmap(thumbnail.getWidth(),
+                 thumbnail.getHeight(),
+                 Bitmap.Config.ARGB_8888);
+         Canvas c = new Canvas(resultBitmap);
+         // compute visual center of play button, according to resized image
+         int x1 = resizedPlayButton.getWidth();
+         int y1 = resizedPlayButton.getHeight() / 2;
+         int x2 = 0;
+         int y2 = resizedPlayButton.getWidth();
+         int x3 = 0;
+         int y3 = 0;
+         double ym = ( ((Math.pow(x3,2) - Math.pow(x1,2) + Math.pow(y3,2) - Math.pow(y1,2)) *
+                     (x2 - x1)) - (Math.pow(x2,2) - Math.pow(x1,2) + Math.pow(y2,2) -
+                     Math.pow(y1,2)) * (x3 - x1) )  /  (2 * ( ((y3 - y1) * (x2 - x1)) -
+                     ((y2 - y1) * (x3 - x1)) ));
+         double xm = ( (Math.pow(x2,2) - Math.pow(x1,2)) + (Math.pow(y2,2) - Math.pow(y1,2)) -
+                     (2*ym*(y2 - y1)) ) / (2*(x2 - x1));
+         // offset to top left
+         double ox = - xm;
+         double oy = thumbnail.getHeight() - ym;
+         c.drawBitmap(thumbnail, 0, 0, null);
+         Paint p = new Paint();
+         p.setAlpha(230);
+         c.drawBitmap(resizedPlayButton, (float) ((thumbnail.getWidth() / 2) + ox),
+                                         (float) ((thumbnail.getHeight() / 2) - ym), p);
+         return resultBitmap;
+     }
      public static class AsyncDrawable extends BitmapDrawable {
          private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference;
  
@@@ -4,7 -4,6 +4,7 @@@
   *   @author Bartek Przybylski\r
   *   @author Tobias Kaminsky\r
   *   @author David A. Velasco\r
 + *   @author masensio\r
   *   Copyright (C) 2011  Bartek Przybylski\r
   *   Copyright (C) 2015 ownCloud Inc.\r
   *\r
@@@ -25,16 -24,15 +25,19 @@@ package com.owncloud.android.ui.adapter
  \r
  \r
  import java.io.File;\r
 +import java.util.ArrayList;\r
 +import java.util.HashMap;\r
 +import java.util.Map;\r
  import java.util.Vector;\r
  \r
  import android.accounts.Account;\r
  import android.content.Context;\r
  import android.content.SharedPreferences;\r
  import android.graphics.Bitmap;\r
 +import android.graphics.Color;\r
+ import android.graphics.BitmapFactory;\r
+ import android.graphics.Canvas;\r
+ import android.graphics.Paint;\r
  import android.os.Build;\r
  import android.preference.PreferenceManager;\r
  import android.text.format.DateUtils;\r
@@@ -59,7 -57,6 +62,7 @@@ import com.owncloud.android.services.Op
  import com.owncloud.android.ui.activity.ComponentsGetter;\r
  import com.owncloud.android.utils.DisplayUtils;\r
  import com.owncloud.android.utils.FileStorageUtils;\r
 +import com.owncloud.android.utils.MimetypeIconUtil;\r
  \r
  \r
  /**\r
@@@ -83,8 -80,6 +86,8 @@@ public class FileListListAdapter extend
      private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM };\r
  \r
      private SharedPreferences mAppPreferences;\r
 +\r
 +    private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();\r
      \r
      public FileListListAdapter(\r
              boolean justFolders, \r
          ViewType viewType;\r
          if (!mGridMode){\r
              viewType = ViewType.LIST_ITEM;\r
 -        } else if (file.isImage()){\r
 +        } else if (file.isImage() || file.isVideo()){\r
              viewType = ViewType.GRID_IMAGE;\r
          } else {\r
              viewType = ViewType.GRID_ITEM;\r
              switch (viewType){\r
                  case LIST_ITEM:\r
                      TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
 +                    TextView fileSizeSeparatorV = (TextView) view.findViewById(R.id.file_separator);\r
                      TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
 -                    ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
 +\r
  \r
                      lastModV.setVisibility(View.VISIBLE);\r
                      lastModV.setText(showRelativeTimestamp(file));\r
  \r
 -                    checkBoxV.setVisibility(View.GONE);\r
  \r
 +                    fileSizeSeparatorV.setVisibility(View.VISIBLE);\r
                      fileSizeV.setVisibility(View.VISIBLE);\r
                      fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
  \r
 -                    if (!file.isFolder()) {\r
 -                        AbsListView parentList = (AbsListView)parent;\r
 -                        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\r
 -                            if (parentList.getChoiceMode() == AbsListView.CHOICE_MODE_NONE) {\r
 -                                checkBoxV.setVisibility(View.GONE);\r
 -                            } else {\r
 -                                if (parentList.isItemChecked(position)) {\r
 -                                    checkBoxV.setImageResource(\r
 -                                            android.R.drawable.checkbox_on_background);\r
 -                                } else {\r
 -                                    checkBoxV.setImageResource(\r
 -                                            android.R.drawable.checkbox_off_background);\r
 -                                }\r
 -                                checkBoxV.setVisibility(View.VISIBLE);\r
 -                            }\r
 -                        }\r
 -\r
 -                    } else { //Folder\r
 +//                    if (!file.isFolder()) {\r
 +//                        AbsListView parentList = (AbsListView)parent;\r
 +//                        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\r
 +//                            if (parentList.getChoiceMode() == AbsListView.CHOICE_MODE_NONE) {\r
 +//                                checkBoxV.setVisibility(View.GONE);\r
 +//                            } else {\r
 +//                                if (parentList.isItemChecked(position)) {\r
 +//                                    checkBoxV.setImageResource(\r
 +//                                            R.drawable.ic_checkbox_marked);\r
 +//                                } else {\r
 +//                                    checkBoxV.setImageResource(\r
 +//                                            R.drawable.ic_checkbox_blank_outline);\r
 +//                                }\r
 +//                                checkBoxV.setVisibility(View.VISIBLE);\r
 +//                            }\r
 +//                        }\r
 +\r
 +                    if (file.isFolder()) {\r
 +                        fileSizeSeparatorV.setVisibility(View.INVISIBLE);\r
                          fileSizeV.setVisibility(View.INVISIBLE);\r
                      }\r
  \r
                              mTransferServiceGetter.getFileDownloaderBinder();\r
                      FileUploaderBinder uploaderBinder =\r
                              mTransferServiceGetter.getFileUploaderBinder();\r
 -                    boolean downloading = (downloaderBinder != null &&\r
 -                            downloaderBinder.isDownloading(mAccount, file));\r
                      OperationsServiceBinder opsBinder =\r
                              mTransferServiceGetter.getOperationsServiceBinder();\r
 -                    downloading |= (opsBinder != null &&\r
 -                            opsBinder.isSynchronizing(mAccount, file.getRemotePath()));\r
 -                    if (downloading) {\r
 -                        localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
 +\r
 +                    localStateView.setVisibility(View.INVISIBLE);   // default first\r
 +\r
 +                    if ( //synchronizing\r
 +                                opsBinder != null &&\r
 +                                opsBinder.isSynchronizing(mAccount, file.getRemotePath())\r
 +                            ) {\r
 +                        localStateView.setImageResource(R.drawable.synchronizing_file_indicator);\r
                          localStateView.setVisibility(View.VISIBLE);\r
 -                    } else if (uploaderBinder != null &&\r
 -                            uploaderBinder.isUploading(mAccount, file)) {\r
 -                        localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
 +\r
 +                    } else if ( // downloading\r
 +                                downloaderBinder != null &&\r
 +                                downloaderBinder.isDownloading(mAccount, file)\r
 +                            ) {\r
 +                        localStateView.setImageResource(\r
 +                                file.isFolder() ?\r
 +                                        R.drawable.synchronizing_file_indicator :\r
 +                                        R.drawable.downloading_file_indicator\r
 +                        );\r
                          localStateView.setVisibility(View.VISIBLE);\r
 +\r
 +                    } else if ( //uploading\r
 +                                uploaderBinder != null &&\r
 +                                uploaderBinder.isUploading(mAccount, file)\r
 +                            ) {\r
 +                        localStateView.setImageResource(\r
 +                                file.isFolder() ?\r
 +                                        R.drawable.synchronizing_file_indicator :\r
 +                                        R.drawable.uploading_file_indicator\r
 +                        );\r
 +                        localStateView.setVisibility(View.VISIBLE);\r
 +\r
 +                    } else if (file.getEtagInConflict() != null) {   // conflict\r
 +                        localStateView.setImageResource(R.drawable.conflict_file_indicator);\r
 +                        localStateView.setVisibility(View.VISIBLE);\r
 +\r
                      } else if (file.isDown()) {\r
                          localStateView.setImageResource(R.drawable.local_file_indicator);\r
                          localStateView.setVisibility(View.VISIBLE);\r
 -                    } else {\r
 -                        localStateView.setVisibility(View.INVISIBLE);\r
                      }\r
  \r
                      // share with me icon\r
  \r
                      break;\r
              }\r
 +\r
 +            ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
 +            checkBoxV.setVisibility(View.GONE);\r
 +\r
 +            AbsListView parentList = (AbsListView)parent;\r
 +            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\r
 +                if (parentList.getChoiceMode() == AbsListView.CHOICE_MODE_NONE) {\r
 +                    checkBoxV.setVisibility(View.GONE);\r
 +                } else if (parentList.getCheckedItemCount() > 0){\r
 +                    if (parentList.isItemChecked(position)) {\r
 +                        checkBoxV.setImageResource(\r
 +                                android.R.drawable.checkbox_on_background);\r
 +                    } else {\r
 +                        checkBoxV.setImageResource(\r
 +                                android.R.drawable.checkbox_off_background);\r
 +                    }\r
 +                    checkBoxV.setVisibility(View.VISIBLE);\r
 +                }\r
 +            }\r
              \r
              // For all Views\r
              \r
              \r
              // No Folder\r
              if (!file.isFolder()) {\r
-                 if (file.isImage() && file.getRemoteId() != null){\r
+                 if ((file.isImage() || file.isVideo()) && file.getRemoteId() != null){\r
                      // Thumbnail in Cache?\r
                      Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(\r
 -                            String.valueOf(file.getRemoteId())\r
 -                            );\r
 +                            "t" + String.valueOf(file.getRemoteId()));\r
                      if (thumbnail != null && !file.needsUpdateThumbnail()){\r
-                         fileIcon.setImageBitmap(thumbnail);\r
\r
+                         if (file.isVideo()) {\r
+                             Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);\r
+                             fileIcon.setImageBitmap(withOverlay);\r
+                         } else {\r
+                             fileIcon.setImageBitmap(thumbnail);\r
+                         }\r
                      } else {\r
                          // generate new Thumbnail\r
                          if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {\r
                                      task\r
                                      );\r
                              fileIcon.setImageDrawable(asyncDrawable);\r
 -                            task.execute(file);\r
 +                            task.execute(file, true);\r
                          }\r
                      }\r
 +\r
 +                    if (file.getMimetype().equalsIgnoreCase("image/png")) {\r
 +                        fileIcon.setBackgroundColor(mContext.getResources()\r
 +                                .getColor(R.color.background_color));\r
 +                    }\r
 +\r
 +\r
                  } else {\r
 -                    fileIcon.setImageResource(DisplayUtils.getFileTypeIconId(file.getMimetype(),\r
 +                    fileIcon.setImageResource(MimetypeIconUtil.getFileTypeIconId(file.getMimetype(),\r
                              file.getFileName()));\r
                  }\r
  \r
              } else {\r
                  // Folder\r
 -                if (checkIfFileIsSharedWithMe(file)) {\r
 -                    fileIcon.setImageResource(R.drawable.shared_with_me_folder);\r
 -                } else if (file.isShareByLink()) {\r
 -                    // If folder is sharedByLink, icon folder must be changed to\r
 -                    // folder-public one\r
 -                    fileIcon.setImageResource(R.drawable.folder_public);\r
 -                } else {\r
 -                    fileIcon.setImageResource(\r
 -                            DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName())\r
 -                    );\r
 -                }\r
 +                fileIcon.setImageResource(\r
 +                        MimetypeIconUtil.getFolderTypeIconId(\r
 +                                checkIfFileIsSharedWithMe(file), file.isShareByLink()));\r
              }\r
          }\r
  \r
 -        return view;\r
 -    }\r
 -\r
 -    /**\r
 -     * Local Folder size in human readable format\r
 -     * \r
 -     * @param path\r
 -     *            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 = FileStorageUtils.getFolderSize(dir);\r
 -            return DisplayUtils.bytesToHumanReadable(bytes);\r
 +        if (mSelection.get(position) != null) {\r
 +            view.setBackgroundColor(Color.rgb(248, 248, 248));\r
 +        } else {\r
 +            view.setBackgroundColor(Color.WHITE);\r
          }\r
  \r
 -        return "0 B";\r
 +        return view;\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
 -\r
      @Override\r
      public int getViewTypeCount() {\r
          return 1;\r
       *                                  mStorageManager if is different (and not NULL)\r
       */\r
      public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager\r
 -            /*, boolean onlyOnDevice*/) {\r
 +            , boolean onlyOnDevice) {\r
          mFile = directory;\r
          if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
              mStorageManager = updatedStorageManager;\r
              mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
          }\r
          if (mStorageManager != null) {\r
 -            // TODO Enable when "On Device" is recovered ?\r
 -            mFiles = mStorageManager.getFolderContent(mFile/*, onlyOnDevice*/);\r
 +            mFiles = mStorageManager.getFolderContent(mFile, onlyOnDevice);\r
              mFilesOrig.clear();\r
              mFilesOrig.addAll(mFiles);\r
              \r
              mFiles = null;\r
          }\r
  \r
 -        mFiles = FileStorageUtils.sortFolder(mFiles);\r
 +        mFiles = FileStorageUtils.sortOcFolder(mFiles);\r
          notifyDataSetChanged();\r
      }\r
      \r
          FileStorageUtils.mSortAscending = ascending;\r
          \r
  \r
 -        mFiles = FileStorageUtils.sortFolder(mFiles);\r
 +        mFiles = FileStorageUtils.sortOcFolder(mFiles);\r
          notifyDataSetChanged();\r
  \r
      }\r
      public void setGridMode(boolean gridMode) {\r
          mGridMode = gridMode;\r
      }\r
 +\r
 +    public boolean isGridMode() {\r
 +        return mGridMode;\r
 +    }\r
 +\r
 +    public void setNewSelection(int position, boolean checked) {\r
 +        mSelection.put(position, checked);\r
 +        notifyDataSetChanged();\r
 +    }\r
 +\r
 +    public void removeSelection(int position) {\r
 +        mSelection.remove(position);\r
 +        notifyDataSetChanged();\r
 +    }\r
 +\r
 +    public void removeSelection(){\r
 +         mSelection.clear();\r
 +        notifyDataSetChanged();\r
 +    }\r
 +\r
 +    public ArrayList<Integer> getCheckedItemPositions() {\r
 +        ArrayList<Integer> ids = new ArrayList<Integer>();\r
 +\r
 +        for (Map.Entry<Integer, Boolean> entry : mSelection.entrySet()){\r
 +            if (entry.getValue()){\r
 +                ids.add(entry.getKey());\r
 +            }\r
 +        }\r
 +        return ids;\r
 +    }\r
 +\r
 +    public ArrayList<OCFile> getCheckedItems() {\r
 +        ArrayList<OCFile> files = new ArrayList<OCFile>();\r
 +\r
 +        for (Map.Entry<Integer, Boolean> entry : mSelection.entrySet()){\r
 +            if (entry.getValue()){\r
 +                files.add((OCFile) getItem(entry.getKey()));\r
 +            }\r
 +        }\r
 +        return files;\r
 +    }\r
  }\r
   */
  package com.owncloud.android.ui.fragment;
  
 -import java.io.File;
 -
 +import android.accounts.Account;
  import android.app.Activity;
 +import android.content.Context;
 +import android.content.DialogInterface;
  import android.content.Intent;
 +import android.content.SharedPreferences;
 +import android.os.Build;
 +import android.net.Uri;
  import android.os.Bundle;
 +import android.preference.PreferenceManager;
  import android.support.v4.widget.SwipeRefreshLayout;
 +import android.view.ActionMode;
 +import android.support.v7.app.AlertDialog;
  import android.view.ContextMenu;
 +import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
  import android.view.View;
 +import android.widget.AbsListView;
  import android.widget.AdapterView;
  import android.widget.AdapterView.AdapterContextMenuInfo;
 +import android.widget.PopupMenu;
 +import android.widget.TextView;
 +import android.widget.Toast;
  
 +import com.owncloud.android.MainApp;
  import com.owncloud.android.R;
  import com.owncloud.android.authentication.AccountUtils;
  import com.owncloud.android.datamodel.FileDataStorageManager;
@@@ -55,34 -42,21 +55,34 @@@ import com.owncloud.android.datamodel.O
  import com.owncloud.android.files.FileMenuFilter;
  import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 +import com.owncloud.android.media.MediaService;
  import com.owncloud.android.ui.activity.FileActivity;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
  import com.owncloud.android.ui.activity.FolderPickerActivity;
  import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
 +import com.owncloud.android.ui.activity.UploadFilesActivity;
  import com.owncloud.android.ui.adapter.FileListListAdapter;
  import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 +import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
 +import com.owncloud.android.ui.dialog.FileActionsDialogFragment;
  import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 +import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
  import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
 +import com.owncloud.android.ui.dialog.UploadSourceDialogFragment;
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  import com.owncloud.android.ui.preview.PreviewMediaFragment;
 +import com.owncloud.android.utils.DisplayUtils;
 +import com.owncloud.android.utils.ExceptionHandler;
 +import com.owncloud.android.utils.FileStorageUtils;
 +import com.owncloud.android.ui.preview.PreviewTextFragment;
  import com.owncloud.android.utils.FileStorageUtils;
  
 +import java.io.File;
 +import java.util.ArrayList;
 +
  /**
   * A Fragment that lists all files and folders in a given path.
 - * 
 + *
   * TODO refactor to get rid of direct dependency on FileDisplayActivity
   */
  public class OCFileListFragment extends ExtendedListFragment {
  
      private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
              OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
 -            
 +
      public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
      public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
 -            
 +    public final static String ARG_HIDE_FAB = MY_PACKAGE + ".HIDE_FAB";
 +
      private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
 +    private static final String KEY_FAB_EVER_CLICKED = "FAB_EVER_CLICKED";
 +
 +    private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
  
      private FileFragment.ContainerActivity mContainerActivity;
 -   
 +
      private OCFile mFile = null;
      private FileListListAdapter mAdapter;
      private boolean mJustFolders;
      
      private OCFile mTargetFile;
 -    
 -   
 +
 +    private boolean miniFabClicked = false;
      
      /**
       * {@inheritDoc}
          Log_OC.e(TAG, "onAttach");
          try {
              mContainerActivity = (FileFragment.ContainerActivity) activity;
 -            
 +
          } catch (ClassCastException e) {
 -            throw new ClassCastException(activity.toString() + " must implement " + 
 +            throw new ClassCastException(activity.toString() + " must implement " +
                      FileFragment.ContainerActivity.class.getSimpleName());
          }
          try {
              setOnRefreshListener((OnEnforceableRefreshListener) activity);
              
          } catch (ClassCastException e) {
 -            throw new ClassCastException(activity.toString() + " must implement " + 
 +            throw new ClassCastException(activity.toString() + " must implement " +
                      SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
          }
      }
  
 -    
 +
      @Override
      public void onDetach() {
          setOnRefreshListener(null);
                  mJustFolders,
                  getActivity(),
                  mContainerActivity
 -                );
 +        );
          setListAdapter(mAdapter);
  
 -        registerForContextMenu();
 +        registerLongClickListener();
 +
 +        boolean hideFab = (args != null) && args.getBoolean(ARG_HIDE_FAB, false);
 +        if (hideFab) {
 +            setFabEnabled(false);
 +        } else {
 +            setFabEnabled(true);
 +            registerFabListeners();
 +
 +            // detect if a mini FAB has ever been clicked
 +            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
 +            if(prefs.getLong(KEY_FAB_EVER_CLICKED, 0) > 0) {
 +                miniFabClicked = true;
 +            }
 +
 +            // add labels to the min FABs when none of them has ever been clicked on
 +            if(!miniFabClicked) {
 +                setFabLabels();
 +            } else {
 +                removeFabLabels();
 +            }
 +        }
    }
  
      /**
 +     * adds labels to all mini FABs.
 +     */
 +    private void setFabLabels() {
 +        getFabUpload().setTitle(getResources().getString(R.string.actionbar_upload));
 +        getFabMkdir().setTitle(getResources().getString(R.string.actionbar_mkdir));
 +        getFabUploadFromApp().setTitle(getResources().getString(R.string.actionbar_upload_from_apps));
 +    }
 +
 +    /**
 +     * registers all listeners on all mini FABs.
 +     */
 +    private void registerFabListeners() {
 +        registerFabUploadListeners();
 +        registerFabMkDirListeners();
 +        registerFabUploadFromAppListeners();
 +    }
 +
 +    /**
 +     * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
 +     * on the Upload mini FAB for the linked action and {@link Toast} showing the underlying action.
 +     */
 +    private void registerFabUploadListeners() {
 +        getFabUpload().setOnClickListener(new View.OnClickListener() {
 +            @Override
 +            public void onClick(View v) {
 +                Intent action = new Intent(getActivity(), UploadFilesActivity.class);
 +                action.putExtra(
 +                        UploadFilesActivity.EXTRA_ACCOUNT,
 +                        ((FileActivity) getActivity()).getAccount()
 +                );
 +                getActivity().startActivityForResult(action, UploadSourceDialogFragment.ACTION_SELECT_MULTIPLE_FILES);
 +                getFabMain().collapse();
 +                recordMiniFabClick();
 +            }
 +        });
 +
 +        getFabUpload().setOnLongClickListener(new View.OnLongClickListener() {
 +            @Override
 +            public boolean onLongClick(View v) {
 +                Toast.makeText(getActivity(), R.string.actionbar_upload, Toast.LENGTH_SHORT).show();
 +                return true;
 +            }
 +        });
 +    }
 +
 +    /**
 +     * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
 +     * on the 'Create Dir' mini FAB for the linked action and {@link Toast} showing the underlying action.
 +     */
 +    private void registerFabMkDirListeners() {
 +        getFabMkdir().setOnClickListener(new View.OnClickListener() {
 +            @Override
 +            public void onClick(View v) {
 +                CreateFolderDialogFragment dialog =
 +                        CreateFolderDialogFragment.newInstance(mFile);
 +                dialog.show(getActivity().getSupportFragmentManager(), FileDisplayActivity.DIALOG_CREATE_FOLDER);
 +                getFabMain().collapse();
 +                recordMiniFabClick();
 +            }
 +        });
 +
 +        getFabMkdir().setOnLongClickListener(new View.OnLongClickListener() {
 +            @Override
 +            public boolean onLongClick(View v) {
 +                Toast.makeText(getActivity(), R.string.actionbar_mkdir, Toast.LENGTH_SHORT).show();
 +                return true;
 +            }
 +        });
 +    }
 +
 +    /**
 +     * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
 +     * on the Upload from App mini FAB for the linked action and {@link Toast} showing the underlying action.
 +     */
 +    private void registerFabUploadFromAppListeners() {
 +        getFabUploadFromApp().setOnClickListener(new View.OnClickListener() {
 +            @Override
 +            public void onClick(View v) {
 +                Intent action = new Intent(Intent.ACTION_GET_CONTENT);
 +                action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
 +
 +                //Intent.EXTRA_ALLOW_MULTIPLE is only supported on api level 18+, Jelly Bean
 +                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
 +                    action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
 +                }
 +
 +                getActivity().startActivityForResult(
 +                        Intent.createChooser(action, getString(R.string.upload_chooser_title)),
 +                        UploadSourceDialogFragment.ACTION_SELECT_CONTENT_FROM_APPS
 +                );
 +                getFabMain().collapse();
 +                recordMiniFabClick();
 +            }
 +        });
 +
 +        getFabUploadFromApp().setOnLongClickListener(new View.OnLongClickListener() {
 +            @Override
 +            public boolean onLongClick(View v) {
 +                Toast.makeText(getActivity(),
 +                        R.string.actionbar_upload_from_apps,
 +                        Toast.LENGTH_SHORT).show();
 +                return true;
 +            }
 +        });
 +    }
 +
 +    /**
 +     * records a click on a mini FAB and thus:
 +     * <ol>
 +     *     <li>persists the click fact</li>
 +     *     <li>removes the mini FAB labels</li>
 +     * </ol>
 +     */
 +    private void recordMiniFabClick() {
 +        // only record if it hasn't been done already at some other time
 +        if(!miniFabClicked) {
 +            final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
 +            sp.edit().putLong(KEY_FAB_EVER_CLICKED, 1).commit();
 +            miniFabClicked = true;
 +        }
 +    }
 +
 +    /**
 +     * removes the labels on all known min FABs.
 +     */
 +    private void removeFabLabels() {
 +        getFabUpload().setTitle(null);
 +        getFabMkdir().setTitle(null);
 +        getFabUploadFromApp().setTitle(null);
 +        ((TextView) getFabUpload().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
 +        ((TextView) getFabMkdir().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
 +        ((TextView) getFabUploadFromApp().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
 +    }
 +
 +    private void registerLongClickListener() {
 +        getListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
 +            private Menu menu;
 +
 +            @Override
 +            public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
 +                final int checkedCount = getListView().getCheckedItemCount();
 +                // TODO Tobi extract to values
 +                mode.setTitle(checkedCount + " selected");
 +
 +                if (checked) {
 +                    mAdapter.setNewSelection(position, checked);
 +                } else {
 +                    mAdapter.removeSelection(position);
 +                }
 +
 +                // TODO maybe change: only recreate menu if count changes
 +                menu.clear();
 +                if (checkedCount == 1) {
 +                    createContextMenu(menu);
 +                } else {
 +                    // download, move, copy, delete
 +                    getActivity().getMenuInflater().inflate(R.menu.multiple_file_actions_menu, menu);
 +                }
 +
 +            }
 +
 +            @Override
 +            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
 +                this.menu = menu;
 +                return true;
 +            }
 +
 +            @Override
 +            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
 +                return false;
 +            }
 +
 +            @Override
 +            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
 +                return onFileActionChosen(item.getItemId());
 +            }
 +
 +            @Override
 +            public void onDestroyActionMode(ActionMode mode) {
 +                mAdapter.removeSelection();
 +            }
 +        });
 +    }
 +
 +
 +    private void showFileAction(int fileIndex) {
 +        Bundle args = getArguments();
 +        PopupMenu pm = new PopupMenu(getActivity(),null);
 +        Menu menu = pm.getMenu();
 +
 +        boolean allowContextualActions =
 +                (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
 +
 +        if (allowContextualActions) {
 +            MenuInflater inflater = getActivity().getMenuInflater();
 +
 +            inflater.inflate(R.menu.file_actions_menu, menu);
 +            OCFile targetFile = (OCFile) mAdapter.getItem(fileIndex);
 +
 +            if (mContainerActivity.getStorageManager() != null) {
 +                FileMenuFilter mf = new FileMenuFilter(
 +                        targetFile,
 +                        mContainerActivity.getStorageManager().getAccount(),
 +                        mContainerActivity,
 +                        getActivity()
 +                );
 +                mf.filter(menu);
 +            }
 +
 +            /// TODO break this direct dependency on FileDisplayActivity... if possible
 +            MenuItem item = menu.findItem(R.id.action_open_file_with);
 +            FileFragment frag = ((FileDisplayActivity)getActivity()).getSecondFragment();
 +            if (frag != null && frag instanceof FileDetailFragment &&
 +                    frag.getFile().getFileId() == targetFile.getFileId()) {
 +                item = menu.findItem(R.id.action_see_details);
 +                if (item != null) {
 +                    item.setVisible(false);
 +                    item.setEnabled(false);
 +                }
 +            }
 +
 +            FileActionsDialogFragment dialog = FileActionsDialogFragment.newInstance(menu, fileIndex, targetFile.getFileName());
 +            dialog.setTargetFragment(this, 0);
 +            dialog.show(getFragmentManager(), FileActionsDialogFragment.FTAG_FILE_ACTIONS);
 +        }
 +    }
 +
 +    /**
       * Saves the current listed folder.
       */
      @Override
 -    public void onSaveInstanceState (Bundle outState) {
 +    public void onSaveInstanceState(Bundle outState) {
          super.onSaveInstanceState(outState);
          outState.putParcelable(KEY_FILE, mFile);
      }
 -    
 +
      /**
       * Call this, when the user presses the up button.
 -     * 
 -     * Tries to move up the current folder one level. If the parent folder was removed from the 
 -     * database, it continues browsing up until finding an existing folders.
 -     * 
 -     * return       Count of folder levels browsed up.
 +     * <p>
 +     *     Tries to move up the current folder one level. If the parent folder was removed from the
 +     *     database, it continues browsing up until finding an existing folders.
 +     * </p>
 +     * @return Count of folder levels browsed up.
       */
      public int onBrowseUp() {
          OCFile parentDir = null;
          int moveCount = 0;
 -        
 -        if(mFile != null){
 +
 +        if (mFile != null) {
              FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
 -            
 +
              String parentPath = null;
              if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
                  parentPath = new File(mFile.getRemotePath()).getParent();
 -                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
 -                      parentPath + OCFile.PATH_SEPARATOR;
 +                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
 +                        parentPath + OCFile.PATH_SEPARATOR;
                  parentDir = storageManager.getFileByPath(parentPath);
                  moveCount++;
              } else {
              }
              while (parentDir == null) {
                  parentPath = new File(parentPath).getParent();
 -                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
 -                      parentPath + OCFile.PATH_SEPARATOR;
 +                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
 +                        parentPath + OCFile.PATH_SEPARATOR;
                  parentDir = storageManager.getFileByPath(parentPath);
                  moveCount++;
              }   // exit is granted because storageManager.getFileByPath("/") never returns null
              mFile = parentDir;
  
 -            // TODO Enable when "On Device" is recovered ?
 -            listDirectory(mFile /*, MainApp.getOnlyOnDevice()*/);
 +            listDirectory(mFile, MainApp.getOnlyOnDevice());
  
              onRefresh(false);
 -            
 +
              // restore index and top position
              restoreIndexAndTopPosition();
 -            
 +
          }   // else - should never happen now
 -   
 +
          return moveCount;
      }
 -    
 +
      @Override
      public void onItemClick(AdapterView<?> l, View v, int position, long id) {
          OCFile file = (OCFile) mAdapter.getItem(position);
          if (file != null) {
 -            if (file.isFolder()) { 
 +            if (file.isFolder()) {
                  // update state and view of this fragment
 -                // TODO Enable when "On Device" is recovered ?
 -                listDirectory(file/*, MainApp.getOnlyOnDevice()*/);
 +                listDirectory(file, MainApp.getOnlyOnDevice());
                  // then, notify parent activity to let it update its state and view
                  mContainerActivity.onBrowsedDownTo(file);
                  // save index and top position
                  saveIndexAndTopPosition(position);
 -                
 +
              } else { /// Click on a file
                  if (PreviewImageFragment.canBePreviewed(file)) {
                      // preview image - it handles the download, if needed
                      ((FileDisplayActivity)mContainerActivity).startImagePreview(file);
 -                    
 -                } else if (file.isDown()) {
 -                    if (PreviewMediaFragment.canBePreviewed(file)) {
 +                } else if (PreviewTextFragment.canBePreviewed(file)){
 +                    ((FileDisplayActivity)mContainerActivity).startTextPreview(file);
 +                } else if (PreviewMediaFragment.canBePreviewed(file)) {
                          // media preview
 -                        ((FileDisplayActivity)mContainerActivity).startMediaPreview(file, 0, true);
 -                    } else {
 +                        ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
 +                    } else if (file.isDown()) {
                          mContainerActivity.getFileOperationsHelper().openFile(file);
 -                    }
 -                    
                  } else {
                      // automatic download, preview on finish
 -                    ((FileDisplayActivity)mContainerActivity).startDownloadForPreview(file);
 +                    ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
                  }
 -                    
              }
 -            
          } else {
              Log_OC.d(TAG, "Null object in ListAdapter!!");
          }
 -        
      }
 -    
 +
      /**
       * {@inheritDoc}
       */
 -    @Override
 -    public void onCreateContextMenu (
 -            ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
 -        super.onCreateContextMenu(menu, v, menuInfo);
 +    // TODO Tobi needed?
 +    public void createContextMenu(Menu menu) {
          Bundle args = getArguments();
 -        boolean allowContextualActions = 
 -                (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true); 
 +        boolean allowContextualActions =
 +                (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
          if (allowContextualActions) {
              MenuInflater inflater = getActivity().getMenuInflater();
              inflater.inflate(R.menu.file_actions_menu, menu);
 -            AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
 -            OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
 -            
 +            OCFile targetFile = null;
 +            if (mAdapter.getCheckedItems().size() == 1){
 +                targetFile = mAdapter.getCheckedItems().get(0);
 +            }
 +
              if (mContainerActivity.getStorageManager() != null) {
                  FileMenuFilter mf = new FileMenuFilter(
                      targetFile,
                      item.setEnabled(false);
                  }
              }
 +
 +//            String.format(mContext.getString(R.string.subject_token),
 +//                    getClient().getCredentials().getUsername(), file.getFileName()));
          }
      }
 -    
 +
 +    public boolean onFileActionChosen(int menuId) {
 +        if (mAdapter.getCheckedItems().size() == 1){
 +            OCFile mTargetFile = mAdapter.getCheckedItems().get(0);
 +
 +            switch (menuId) {
 +                case R.id.action_share_file: {
 +                    mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_open_file_with: {
 +                    mContainerActivity.getFileOperationsHelper().openFile(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_unshare_file: {
 +                    mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_rename_file: {
 +                    RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
 +                    dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
 +                    return true;
 +                }
 +                case R.id.action_remove_file: {
 +                    RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
 +                    dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
 +                    return true;
 +                }
 +                case R.id.action_download_file:
 +                case R.id.action_sync_file: {
 +                    mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_cancel_sync: {
 +                    ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_see_details: {
 +                    mContainerActivity.showDetails(mTargetFile);
 +                    return true;
 +                }
 +                case R.id.action_send_file: {
 +                    // Obtain the file
 +                    if (!mTargetFile.isDown()) {  // Download the file
 +                        Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
 +                        ((FileDisplayActivity) mContainerActivity).startDownloadForSending(mTargetFile);
 +                        return true;
 +                    } else {
 +                        mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
 +                    }
 +                }
 +                case R.id.action_stream_file: {
 +                    Account account = ((FileActivity)mContainerActivity).getAccount();
 +                    Context context = MainApp.getAppContext();
 +                    String uri = PreviewMediaFragment.generateUrlWithCredentials(account, context, mTargetFile);
 +                    MediaService.streamWithExternalApp(uri, getActivity()).show();
 +                    return true;
 +                }
 +                case R.id.action_move: {
 +                    Intent action = new Intent(getActivity(), FolderPickerActivity.class);
 +                    ArrayList files = new ArrayList();
 +                    files.add(mTargetFile);
 +                    action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, files);
 +                    getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
 +                    return true;
 +                }
 +                case R.id.action_favorite_file: {
 +                    mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, true);
 +                    return true;
 +                }
 +                case R.id.action_unfavorite_file: {
 +                    mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
 +                    return true;
 +                }
 +                case R.id.action_copy:
 +                    Intent action = new Intent(getActivity(), FolderPickerActivity.class);
 +
 +                    // Pass mTargetFile that contains info of selected file/folder
 +                    action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
 +                    getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
 +                    return true;
 +                default:
 +                    return false;
 +            }
 +        } else {
 +            ArrayList<OCFile> mTargetFiles = mAdapter.getCheckedItems();
 +
 +            switch (menuId) {
 +                case R.id.action_remove_file: {
 +                    RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(mTargetFiles);
 +                    dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
 +                    return true;
 +                }
 +                case R.id.action_download_file:
 +                case R.id.action_sync_file: {
 +                    mContainerActivity.getFileOperationsHelper().syncFiles(mTargetFiles);
 +                    return true;
 +                }
 +                case R.id.action_move: {
 +                    Intent action = new Intent(getActivity(), FolderPickerActivity.class);
 +                    action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
 +                    getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
 +                    return true;
 +                }
 +                case R.id.action_favorite_file: {
 +                    mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, true);
 +                    return true;
 +                }
 +                case R.id.action_unfavorite_file: {
 +                    mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, false);
 +                    return true;
 +                }
 +                case R.id.action_copy:
 +                    Intent action = new Intent(getActivity(), FolderPickerActivity.class);
 +                    action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
 +                    getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
 +                    return true;
 +                default:
 +                    return false;
 +            }
 +        }
 +
 +    }
      
      /**
       * {@inhericDoc}
       */
      @Override
      public boolean onContextItemSelected (MenuItem item) {
 -        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();        
 -        mTargetFile = (OCFile) mAdapter.getItem(info.position);
 -        switch (item.getItemId()) {                
 -            case R.id.action_share_file: {
 -                mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_open_file_with: {
 -                mContainerActivity.getFileOperationsHelper().openFile(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_unshare_file: {
 -                mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_rename_file: {
 -                RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
 -                dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
 -                return true;
 -            }
 -            case R.id.action_remove_file: {
 -                RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
 -                dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
 -                return true;
 -            }
 -            case R.id.action_download_file: 
 -            case R.id.action_sync_file: {
 -                mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_cancel_download:
 -            case R.id.action_cancel_upload: {
 -                ((FileDisplayActivity)mContainerActivity).cancelTransference(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_see_details: {
 -                mContainerActivity.showDetails(mTargetFile);
 -                return true;
 -            }
 -            case R.id.action_send_file: {
 -                // Obtain the file
 -                if (!mTargetFile.isDown()) {  // Download the file
 -                    Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
 -                    ((FileDisplayActivity)mContainerActivity).startDownloadForSending(mTargetFile);
 -                    
 -                } else {
 -                    mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
 -                }
 -                return true;
 -            }
 -            case R.id.action_move: {
 -                Intent action = new Intent(getActivity(), FolderPickerActivity.class);
 -
 -                // Pass mTargetFile that contains info of selected file/folder
 -                action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
 -                getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
 -                return true;
 -            }
 -            case R.id.action_favorite_file:{
 -                mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, true);
 -                return true;
 -            }
 -            case R.id.action_unfavorite_file:{
 -                mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
 -                return true;
 -            }
 -            default:
 -                return super.onContextItemSelected(item); 
 +        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
 +        boolean matched = onFileActionChosen(item.getItemId());
 +        if(!matched) {
 +            return super.onContextItemSelected(item);
 +        } else {
 +            return matched;
          }
      }
  
      /**
       * Use this to query the {@link OCFile} that is currently
       * being displayed by this fragment
 +     *
       * @return The currently viewed OCFile
       */
 -    public OCFile getCurrentFile(){
 +    public OCFile getCurrentFile() {
          return mFile;
      }
 -    
 +
      /**
 -     * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
 +     * Calls {@link OCFileListFragment#listDirectory(OCFile, boolean)} with a null parameter
       */
 -    public void listDirectory(/*boolean onlyOnDevice*/){
 -        listDirectory(null);
 -        // TODO Enable when "On Device" is recovered ?
 -        // listDirectory(null, onlyOnDevice);
 +    public void listDirectory(boolean onlyOnDevice){
 +        listDirectory(null, onlyOnDevice);
      }
      
      public void refreshDirectory(){
 -        // TODO Enable when "On Device" is recovered ?
 -        listDirectory(getCurrentFile()/*, MainApp.getOnlyOnDevice()*/);
 +        listDirectory(getCurrentFile(), MainApp.getOnlyOnDevice());
      }
 -    
 +
      /**
       * Lists the given directory on the view. When the input parameter is null,
       * it will either refresh the last known directory. list the root
       * if there never was a directory.
 -     * 
 +     *
       * @param directory File to be listed
       */
 -    public void listDirectory(OCFile directory/*, boolean onlyOnDevice*/) {
 +    public void listDirectory(OCFile directory, boolean onlyOnDevice) {
          FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
          if (storageManager != null) {
  
              // Check input parameters for null
 -            if(directory == null){
 -                if(mFile != null){
 +            if (directory == null) {
 +                if (mFile != null) {
                      directory = mFile;
                  } else {
                      directory = storageManager.getFileByPath("/");
                      if (directory == null) return; // no files, wait for sync
                  }
              }
 -        
 -        
 +
 +
              // If that's not a directory -> List its parent
 -            if(!directory.isFolder()){
 +            if (!directory.isFolder()) {
                  Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
                  directory = storageManager.getFileById(directory.getParentId());
              }
  
 -            // TODO Enable when "On Device" is recovered ?
 -            mAdapter.swapDirectory(directory, storageManager/*, onlyOnDevice*/);
 +            mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
              if (mFile == null || !mFile.equals(directory)) {
                  mCurrentListView.setSelection(0);
              }
                  if (file.isFolder()) {
                      foldersCount++;
                  } else {
 -                    filesCount++;
 -                    if (file.isImage() || file.isVideo()){
 -                        imagesCount++;
 +                    if (!file.isHidden()) {
 +                        filesCount++;
 +
-                         if (file.isImage()) {
++                        if (file.isImage() || file.isVideo()) {
 +                            imagesCount++;
 +                        }
                      }
                  }
              }
              OwnCloudVersion version = AccountUtils.getServerVersion(
                      ((FileActivity)mContainerActivity).getAccount());
              if (version != null && version.supportsRemoteThumbnails() &&
 -                imagesCount > 0 && imagesCount == filesCount) {
 +                    DisplayUtils.isGridView(mFile, mContainerActivity.getStorageManager())) {
                  switchToGridView();
 +                registerLongClickListener();
              } else {
                  switchToListView();
+ //                switchToGridView();
              }
          }
      }
          return output;
      }
  
 -
      public void sortByName(boolean descending) {
          mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
      }
      public void sortBySize(boolean descending) {
          mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
      }
 -
 -    
  }