Merge remote-tracking branch 'remotes/upstream/material_fab' into beta
authortobiasKaminsky <tobias@kaminsky.me>
Thu, 29 Oct 2015 17:09:14 +0000 (18:09 +0100)
committertobiasKaminsky <tobias@kaminsky.me>
Thu, 29 Oct 2015 17:09:14 +0000 (18:09 +0100)
1  2 
build.gradle
res/menu/main_menu.xml
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/fragment/ExtendedListFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/com/owncloud/android/utils/DisplayUtils.java

diff --combined build.gradle
@@@ -24,16 -24,12 +24,17 @@@ dependencies 
      compile project(':owncloud-android-library')
      compile 'com.jakewharton:disklrucache:2.0.2'
      compile 'com.android.support:appcompat-v7:22.2.1'
+     compile 'com.getbase:floatingactionbutton:1.10.1'
  }
  
  android {
      compileSdkVersion 22
      buildToolsVersion "22.0.1"
 +
 +    defaultConfig {
 +        applicationId "com.owncloud.android.beta"
 +    }
 +
      sourceSets {
          main {
              manifest.srcFile 'AndroidManifest.xml'
diff --combined res/menu/main_menu.xml
      xmlns:app="http://schemas.android.com/apk/res-auto">
  
      <item
-         android:id="@+id/action_upload"
-         android:icon="@drawable/ic_action_upload"
-         android:orderInCategory="2"
-         app:showAsAction="always"
-         android:title="@string/actionbar_upload"
-         android:contentDescription="@string/actionbar_upload"/>
-     <item
          android:id="@+id/action_create_dir"
          android:icon="@drawable/ic_action_create_dir"
-         android:orderInCategory="2"
+         android:orderInCategory="1"
          app:showAsAction="always"
          android:title="@string/actionbar_mkdir"
          android:contentDescription="@string/actionbar_mkdir"/>
      <item
 +        android:id="@+id/action_switch_view"
 +        android:icon="@drawable/ic_view_module"
 +        android:orderInCategory="2"
 +        app:showAsAction="never"
 +        android:title="@string/action_switch_grid_view" />
 +    <item
          android:id="@+id/action_sync_account"
          android:icon="@drawable/ic_action_refresh"
-         android:orderInCategory="2"
-         app:showAsAction="never"
+         android:orderInCategory="1"
+         app:showAsAction="always"
          android:title="@string/actionbar_sync"
          android:contentDescription="@string/actionbar_sync"/>
        <item
          android:id="@+id/action_sort"
-         android:icon="@android:drawable/ic_menu_sort_by_size"
-         android:orderInCategory="2"
-         app:showAsAction="never"
+         android:icon="@drawable/ic_sort_variant"
+         android:orderInCategory="1"
+         app:showAsAction="ifRoom"
          android:title="@string/actionbar_sort"
          android:contentDescription="@string/actionbar_sort"/>
  
@@@ -149,14 -149,13 +149,14 @@@ public class FileDisplayActivity extend
      private boolean mSyncInProgress = false;
  
      private static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
-     private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
+     public static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
      private static String DIALOG_UPLOAD_SOURCE = "DIALOG_UPLOAD_SOURCE";
      private static String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED";
  
      private OCFile mWaitingToSend;
 +    private Menu mOptionsMenu;
 +
  
 -    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          Log_OC.v(TAG, "onCreate() start");
              setFile(file);
  
              if (mAccountWasSet) {
-                 RelativeLayout navigationDrawerLayout = (RelativeLayout) findViewById(R.id.left_drawer);
-                 if (navigationDrawerLayout != null && getAccount() != null) {
-                     TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
-                     int lastAtPos = getAccount().name.lastIndexOf("@");
-                     username.setText(getAccount().name.substring(0, lastAtPos));
-                 }
+                 setUsernameInDrawer((RelativeLayout) findViewById(R.id.left_drawer), getAccount());
              }
  
              if (!stateWasRecovered) {
                      startTextPreview(file);
              }
  
 +            if (DisplayUtils.isGridView(getFile(), getStorageManager())){
 +                switchToGridView();
 +            } else {
 +                switchToListView();
 +            }
 +
          } else {
              Log_OC.wtf(TAG, "initFragments() called with invalid NULLs!");
              if (getAccount() == null) {
      @Override
      public boolean onPrepareOptionsMenu(Menu menu) {
          boolean drawerOpen = mDrawerLayout.isDrawerOpen(GravityCompat.START);
-         menu.findItem(R.id.action_upload).setVisible(!drawerOpen);
-         menu.findItem(R.id.action_create_dir).setVisible(!drawerOpen);
          menu.findItem(R.id.action_sort).setVisible(!drawerOpen);
          menu.findItem(R.id.action_sync_account).setVisible(!drawerOpen);
 +        menu.findItem(R.id.action_switch_view).setVisible(!drawerOpen);
          
          return super.onPrepareOptionsMenu(menu);
      }
      public boolean onCreateOptionsMenu(Menu menu) {
          MenuInflater inflater = getMenuInflater();
          inflater.inflate(R.menu.main_menu, menu);
+         menu.findItem(R.id.action_create_dir).setVisible(false);
 +        mOptionsMenu = menu;
 +
 +        MenuItem menuItem = mOptionsMenu.findItem(R.id.action_switch_view);
 +
 +        changeGridIcon();
 +
          return true;
      }
      
      public boolean onOptionsItemSelected(MenuItem item) {
          boolean retval = true;
          switch (item.getItemId()) {
-             case R.id.action_create_dir: {
-                 CreateFolderDialogFragment dialog =
-                         CreateFolderDialogFragment.newInstance(getCurrentDir());
-                 dialog.show(getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
-                 break;
-             }
              case R.id.action_sync_account: {
                  startSynchronization();
                  break;
              }
-             case R.id.action_upload: {
-                 UploadSourceDialogFragment dialog =
-                         UploadSourceDialogFragment.newInstance(getAccount());
-                 dialog.show(getSupportFragmentManager(), DIALOG_UPLOAD_SOURCE);
-                 break;
-             }
              case android.R.id.home: {
                  FileFragment second = getSecondFragment();
                  OCFile currentDir = getCurrentDir();
                  builder.create().show();
                  break;
              }
 +            case R.id.action_switch_view:{
 +                if (isGridView()){
 +                    item.setTitle(getApplicationContext().getString(R.string.action_switch_grid_view));
 +                    item.setIcon(ContextCompat.getDrawable(getApplicationContext(),
 +                            R.drawable.ic_view_module));
 +                    DisplayUtils.setViewMode(getFile(), false);
 +                    switchToListView();
 +                } else {
 +                    item.setTitle(getApplicationContext().getString(R.string.action_switch_list_view));
 +                    item.setIcon(ContextCompat.getDrawable(getApplicationContext(),
 +                            R.drawable.ic_view_list));
 +                    DisplayUtils.setViewMode(getFile(), true);
 +                    switchToGridView();
 +                }
 +
 +                return true;
 +            }
          default:
              retval = super.onOptionsItemSelected(item);
          }
          return retval;
      }
  
+     public void createFolder() {
+         CreateFolderDialogFragment dialog =
+                 CreateFolderDialogFragment.newInstance(getCurrentDir());
+         dialog.show(getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
+     }
+     public void uploadLocalFilesSelected() {
+         Intent action = new Intent(this, UploadFilesActivity.class);
+         action.putExtra(
+                 UploadFilesActivity.EXTRA_ACCOUNT,
+                 getAccount()
+         );
+         startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
+     }
+     public void uploadFromOtherAppsSelected() {
+         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);
+         }
+         startActivityForResult(
+                 Intent.createChooser(action, getString(R.string.upload_chooser_title)),
+                 ACTION_SELECT_CONTENT_FROM_APPS
+         );
+     }
      private void startSynchronization() {
          Log_OC.d(TAG, "Got to start sync");
          if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
  
      @Override
      public void onBackPressed() {
-         if (!isDrawerOpen()){
+         boolean isFabOpen = isFabOpen();
+         boolean isDrawerOpen = isDrawerOpen();
+         /*
+          * BackPressed priority/hierarchy:
+          *    1. close drawer if opened
+          *    2. close FAB if open (only if drawer isn't open)
+          *    3. navigate up (only if drawer and FAB aren't open)
+          */
+         if(isDrawerOpen && isFabOpen) {
+             // close drawer first
+             super.onBackPressed();
+         } else if(isDrawerOpen && !isFabOpen) {
+             // close drawer
+             super.onBackPressed();
+         } else if (!isDrawerOpen && isFabOpen) {
+             // close fab
+             getListOfFilesFragment().getFabMain().collapse();
+         } else {
+             // all closed
              OCFileListFragment listOfFiles = getListOfFilesFragment();
              if (mDualPane || getSecondFragment() == null) {
                  OCFile currentDir = getCurrentDir();
                  setFile(listOfFiles.getCurrentFile());
              }
              cleanSecondFragment();
 +            changeGridIcon();
-         } else {
-             super.onBackPressed();
 +        }
 +    }
 +
 +    private void changeGridIcon(){
 +        MenuItem menuItem = mOptionsMenu.findItem(R.id.action_switch_view);
 +        if (DisplayUtils.isGridView(getFile(), getStorageManager())){
 +            menuItem.setTitle(getApplicationContext().getString(R.string.action_switch_list_view));
 +            menuItem.setIcon(ContextCompat.getDrawable(getApplicationContext(),
 +                    R.drawable.ic_view_list));
 +        } else {
 +            menuItem.setTitle(getApplicationContext().getString(R.string.action_switch_grid_view));
 +            menuItem.setIcon(ContextCompat.getDrawable(getApplicationContext(),
 +                    R.drawable.ic_view_module));
          }
      }
  
          Log_OC.v(TAG, "onPause() end");
      }
  
+     public boolean isFabOpen() {
+         if(getListOfFilesFragment() != null && getListOfFilesFragment().getFabMain() != null && getListOfFilesFragment().getFabMain().isExpanded()) {
+             return true;
+         } else {
+             return false;
+         }
+     }
  
      private class SyncBroadcastReceiver extends BroadcastReceiver {
  
          cleanSecondFragment();
          // Sync Folder
          startSyncFolderOperation(directory, false);
 +
 +        MenuItem menuItem = mOptionsMenu.findItem(R.id.action_switch_view);
 +
 +        changeGridIcon();
 +        if (DisplayUtils.isGridView(directory, getStorageManager())){
 +            switchToGridView();
 +        } else {
 +            switchToListView();
 +        }
      }
  
      /**
      private void sortByName(boolean ascending) {
          getListOfFilesFragment().sortByName(ascending);
      }
 +    private boolean isGridView(){ return getListOfFilesFragment().isGridView(); }
 +    private void switchToGridView() {
 +        getListOfFilesFragment().switchToGridView();
 +    }
 +    private void switchToListView() {
 +        getListOfFilesFragment().switchToListView();
 +    }
  
     public void allFilesOption() {
         browseToRoot();
@@@ -194,6 -194,7 +194,7 @@@ public class FileListListAdapter extend
              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
                      checkBoxV.setVisibility(View.GONE);\r
  \r
+                     fileSizeSeparatorV.setVisibility(View.VISIBLE);\r
                      fileSizeV.setVisibility(View.VISIBLE);\r
                      fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
  \r
                              } else {\r
                                  if (parentList.isItemChecked(position)) {\r
                                      checkBoxV.setImageResource(\r
-                                             android.R.drawable.checkbox_on_background);\r
+                                             R.drawable.ic_checkbox_marked);\r
                                  } else {\r
                                      checkBoxV.setImageResource(\r
-                                             android.R.drawable.checkbox_off_background);\r
+                                             R.drawable.ic_checkbox_blank_outline);\r
                                  }\r
                                  checkBoxV.setVisibility(View.VISIBLE);\r
                              }\r
                          }\r
  \r
                      } else { //Folder\r
+                         fileSizeSeparatorV.setVisibility(View.INVISIBLE);\r
                          fileSizeV.setVisibility(View.INVISIBLE);\r
                      }\r
  \r
                                      task\r
                                      );\r
                              fileIcon.setImageDrawable(asyncDrawable);\r
 -                            task.execute(file);\r
 +                            task.execute(file, true);\r
                          }\r
                      }\r
  \r
      public void setGridMode(boolean gridMode) {\r
          mGridMode = gridMode;\r
      }\r
 +\r
 +    public boolean isGridMode() {\r
 +        return mGridMode;\r
 +    }\r
  }\r
@@@ -22,6 -22,7 +22,7 @@@ package com.owncloud.android.ui.fragmen
  
  import java.util.ArrayList;
  
+ import android.app.Activity;
  import android.os.Build;
  import android.os.Bundle;
  import android.support.v4.app.Fragment;
@@@ -36,12 -37,16 +37,16 @@@ import android.widget.GridView
  import android.widget.ListAdapter;
  import android.widget.ListView;
  import android.widget.TextView;
+ import android.widget.Toast;
  
+ import com.getbase.floatingactionbutton.FloatingActionButton;
+ import com.getbase.floatingactionbutton.FloatingActionsMenu;
  import com.owncloud.android.R;
  import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.ui.ExtendedListView;
  import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
  import com.owncloud.android.ui.adapter.FileListListAdapter;
+ import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
  
  import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
  
@@@ -64,6 -69,11 +69,11 @@@ public class ExtendedListFragment exten
      private SwipeRefreshLayout mRefreshGridLayout;
      private SwipeRefreshLayout mRefreshEmptyLayout;
      private TextView mEmptyListMessage;
+     private FloatingActionsMenu fabMain;
+     private FloatingActionButton fabUpload;
+     private FloatingActionButton fabMkdir;
+     private FloatingActionButton fabUploadFromApp;
      
      // Save the state of the scroll in browsing
      private ArrayList<Integer> mIndexes;
          return mCurrentListView;
      }
  
+     public FloatingActionButton getFabUpload() {
+         return fabUpload;
+     }
+     public FloatingActionButton getFabUploadFromApp() {
+         return fabUploadFromApp;
+     }
+     public FloatingActionButton getFabMkdir() {
+         return fabMkdir;
+     }
+     public FloatingActionsMenu getFabMain() {
+         return fabMain;
+     }
  
 -    protected void switchToGridView() {
 +    public void switchToGridView() {
          if ((mCurrentListView == mListView)) {
  
              mListView.setAdapter(null);
              mCurrentListView = mGridView;
          }
      }
 -    
 -    protected void switchToListView() {
 +
 +    public void switchToListView() {
          if (mCurrentListView == mGridView) {
              mGridView.setAdapter(null);
              mRefreshGridLayout.setVisibility(View.GONE);
              mCurrentListView = mListView;
          }
      }
 +
 +    public boolean isGridView(){
 +        if (mAdapter instanceof FileListListAdapter) {
 +            return ((FileListListAdapter) mAdapter).isGridMode();
 +        }
 +        return false;
 +    }
      
      
      @Override
  
          mCurrentListView = mListView;   // list as default
  
+         fabMain = (FloatingActionsMenu) v.findViewById(R.id.fab_main);
+         fabUpload = (FloatingActionButton) v.findViewById(R.id.fab_upload);
+         fabMkdir = (FloatingActionButton) v.findViewById(R.id.fab_mkdir);
+         fabUploadFromApp = (FloatingActionButton) v.findViewById(R.id.fab_upload_from_app);
          return v;
      }
  
      }
  
      /**
+      * Disables FAB.
+      *
+      * Sets the 'visibility' state of the FAB contained in the fragment.
+      *
+      * When 'false' is set, FAB visibility is set to View.GONE programatically,
+      *
+      * @param   enabled     Desired visibility for the FAB.
+      */
+     public void setFabEnabled(boolean enabled) {
+         if(enabled) {
+             fabMain.setVisibility(View.VISIBLE);
+         } else {
+             fabMain.setVisibility(View.GONE);
+         }
+     }
+     /**
       * Set message for empty list view
       */
      public void setMessageForEmptyList(String message) {
@@@ -24,7 -24,10 +24,10 @@@ package com.owncloud.android.ui.fragmen
  
  import android.app.Activity;
  import android.content.Intent;
+ import android.content.SharedPreferences;
+ import android.os.Build;
  import android.os.Bundle;
+ import android.preference.PreferenceManager;
  import android.support.v4.widget.SwipeRefreshLayout;
  import android.view.ContextMenu;
  import android.view.Menu;
@@@ -34,6 -37,8 +37,8 @@@ import android.view.View
  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.R;
  import com.owncloud.android.authentication.AccountUtils;
@@@ -46,14 -51,16 +51,17 @@@ import com.owncloud.android.ui.activity
  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.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.FileStorageUtils;
  import com.owncloud.android.ui.preview.PreviewTextFragment;
  
@@@ -73,8 -80,12 +81,12 @@@ public class OCFileListFragment extend
  
      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;
  
@@@ -83,8 -94,8 +95,8 @@@
      private boolean mJustFolders;
      
      private OCFile mTargetFile;
-     
-    
+     private boolean miniFabClicked = false;
      
      /**
       * {@inheritDoc}
          setListAdapter(mAdapter);
  
          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().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
              public boolean onItemLongClick(AdapterView<?> arg0, View v,
          });
      }
  
      private void showFileAction(int fileIndex) {
          Bundle args = getArguments();
          PopupMenu pm = new PopupMenu(getActivity(),null);
  
      /**
       * 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.
-      * <p/>
-      * 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;
                      } else {
                          mContainerActivity.getFileOperationsHelper().openFile(file);
                      }
 -
 -                } else {
 -                    // automatic download, preview on finish
 -                    ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
 +                    
                  }
 -
              }
  
          } else {
                      item.setEnabled(false);
                  }
              }
 +
 +//            String.format(mContext.getString(R.string.subject_token),
 +//                    getClient().getCredentials().getUsername(), file.getFileName()));
          }
      }
  
              OwnCloudVersion version = AccountUtils.getServerVersion(
                      ((FileActivity)mContainerActivity).getAccount());
              if (version != null && version.supportsRemoteThumbnails() &&
 -                imagesCount > 0 && imagesCount == filesCount) {
 +                    DisplayUtils.isGridView(mFile, mContainerActivity.getStorageManager())) {
                  switchToGridView();
                  registerLongClickListener();
              } else {
  \r
  package com.owncloud.android.utils;\r
  \r
 +import java.io.File;\r
 +import java.net.IDN;\r
 +import java.text.DateFormat;\r
 +import java.util.Arrays;\r
 +import java.util.Calendar;\r
 +import java.util.Date;\r
 +import java.util.HashMap;\r
 +import java.util.HashSet;\r
 +import java.util.Set;\r
 +import java.util.Vector;\r
 +\r
  import android.annotation.TargetApi;\r
  import android.app.Activity;\r
  import android.content.Context;\r
 +import android.content.SharedPreferences;\r
  import android.graphics.Point;\r
  import android.graphics.PorterDuff;\r
  import android.os.Build;\r
@@@ -47,9 -35,9 +47,10 @@@ import android.widget.SeekBar
  \r
  import com.owncloud.android.MainApp;\r
  import com.owncloud.android.R;\r
 +import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.datamodel.OCFile;\r
  \r
+ import java.math.BigDecimal;\r
  import java.net.IDN;\r
  import java.text.DateFormat;\r
  import java.util.Calendar;\r
@@@ -65,6 -53,7 +66,7 @@@ public class DisplayUtils 
      private static final String OWNCLOUD_APP_NAME = "ownCloud";\r
      \r
      private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };\r
+     private static final int[] sizeScales = { 0, 0, 0, 1, 1, 2, 2, 2, 2 };\r
  \r
      private static Map<String, String> mimeType2HumanReadable;\r
  \r
  \r
      /**\r
       * Converts the file size in bytes to human readable output.\r
-      * \r
+      * <ul>\r
+      *     <li>appends a size suffix, e.g. B, KB, MB etc.</li>\r
+      *     <li>rounds the size based on the suffix to 0,1 or 2 decimals</li>\r
+      * </ul>\r
+      *\r
       * @param bytes Input file size\r
       * @return Like something readable like "12 MB"\r
       */\r
      public static String bytesToHumanReadable(long bytes) {\r
          double result = bytes;\r
-         int attachedsuff = 0;\r
-         while (result > 1024 && attachedsuff < sizeSuffixes.length) {\r
+         int attachedSuff = 0;\r
+         while (result > 1024 && attachedSuff < sizeSuffixes.length) {\r
              result /= 1024.;\r
-             attachedsuff++;\r
+             attachedSuff++;\r
          }\r
-         result = ((int) (result * 100)) / 100.;\r
-         return result + " " + sizeSuffixes[attachedsuff];\r
\r
+         return new BigDecimal(result).setScale(\r
+                 sizeScales[attachedSuff], BigDecimal.ROUND_HALF_UP) + " " + sizeSuffixes[attachedSuff];\r
      }\r
  \r
      /**\r
                  dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags);\r
              }\r
          }\r
-         \r
-         return dateString.toString().split(",")[0];\r
\r
+         String[] parts = dateString.toString().split(",");\r
+         if (parts.length == 2) {\r
+             if (parts[1].contains(":") && !parts[0].contains(":")) {\r
+                 return parts[0];\r
+             } else if (parts[0].contains(":") && !parts[1].contains(":")) {\r
+                 return parts[1];\r
+             }\r
+         }\r
+         //dateString contains unexpected format. use localized, absolute date.\r
+         return DisplayUtils.unixTimeToHumanReadable(time);\r
      }\r
  \r
      /**\r
      }\r
  \r
      /**\r
 +     * Determines if user set folder to grid or list view. If folder is not set itself,\r
 +     * it finds a parent that is set (at least root is set).\r
 +     * @param file\r
 +     * @param storageManager\r
 +     * @return\r
 +     */\r
 +    public static boolean isGridView(OCFile file, FileDataStorageManager storageManager){\r
 +        if (file != null) {\r
 +            OCFile fileToTest = file;\r
 +            OCFile parentDir = null;\r
 +            String parentPath = null;\r
 +\r
 +            SharedPreferences setting = MainApp.getAppContext().getSharedPreferences(\r
 +                    "viewMode", Context.MODE_PRIVATE);\r
 +\r
 +            if (setting.contains(fileToTest.getRemoteId())) {\r
 +                return setting.getBoolean(fileToTest.getRemoteId(), false);\r
 +            } else {\r
 +                do {\r
 +                    if (fileToTest.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {\r
 +                        parentPath = new File(fileToTest.getRemotePath()).getParent();\r
 +                        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :\r
 +                                parentPath + OCFile.PATH_SEPARATOR;\r
 +                        parentDir = storageManager.getFileByPath(parentPath);\r
 +                    } else {\r
 +                        parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);\r
 +                    }\r
 +\r
 +                    while (parentDir == null) {\r
 +                        parentPath = new File(parentPath).getParent();\r
 +                        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :\r
 +                                parentPath + OCFile.PATH_SEPARATOR;\r
 +                        parentDir = storageManager.getFileByPath(parentPath);\r
 +                    }\r
 +                    fileToTest = parentDir;\r
 +                } while (endWhile(parentDir, setting));\r
 +                return setting.getBoolean(fileToTest.getRemoteId(), false);\r
 +            }\r
 +        } else {\r
 +            return false;\r
 +        }\r
 +    }\r
 +\r
 +    private static boolean endWhile(OCFile parentDir, SharedPreferences setting) {\r
 +        if (parentDir.getRemotePath().compareToIgnoreCase(OCFile.ROOT_PATH) == 0) {\r
 +            return false;\r
 +        } else {\r
 +            return !setting.contains(parentDir.getRemoteId());\r
 +        }\r
 +    }\r
 +\r
 +    public static void setViewMode(OCFile file, boolean setGrid){\r
 +        SharedPreferences setting = MainApp.getAppContext().getSharedPreferences(\r
 +                "viewMode", Context.MODE_PRIVATE);\r
 +\r
 +        SharedPreferences.Editor editor = setting.edit();\r
 +        editor.putBoolean(file.getRemoteId(), setGrid);\r
 +        editor.commit();\r
 +    }\r
 +\r
 +    /**\r
       * sets the coloring of the given progress bar to color_accent.\r
       *\r
       * @param progressBar the progress bar to be colored\r