Merge remote-tracking branch 'remotes/upstream/avoidDuplicateFiles' into beta
authortobiasKaminsky <tobias@kaminsky.me>
Wed, 2 Dec 2015 19:28:56 +0000 (20:28 +0100)
committertobiasKaminsky <tobias@kaminsky.me>
Wed, 2 Dec 2015 19:28:56 +0000 (20:28 +0100)
1  2 
res/values/strings.xml
res/xml/preferences.xml
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/UploadFileOperation.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/activity/Preferences.java
tests/project.properties

diff --combined res/values/strings.xml
@@@ -23,7 -23,8 +23,7 @@@
      <!-- TODO re-enable when "Accounts" is available in Navigation Drawer -->
      <!--<string name="drawer_item_accounts">Accounts</string>-->
      <string name="drawer_item_all_files">All files</string>
 -    <!-- TODO re-enable when "On Device" is available
 -    <string name="drawer_item_on_device">On device</string>-->
 +    <string name="drawer_item_on_device">On device</string>
      <string name="drawer_item_settings">Settings</string>
      <string name="drawer_item_logs">Logs</string>
        <string name="drawer_close">Close</string>
@@@ -61,7 -62,7 +61,7 @@@
      <string name="setup_btn_connect">Connect</string>
      <string name="uploader_btn_upload_text">Upload</string>
      <string name="uploader_btn_new_folder_text">New folder</string>
 -    <string name="uploader_top_message">Choose upload folder:</string>
 +    <string name="uploader_top_message">Choose upload folder</string>
      <string name="uploader_wrn_no_account_title">No account found</string>
      <string name="uploader_wrn_no_account_text">There are no %1$s accounts on your device. Please setup an account first.</string>
      <string name="uploader_wrn_no_account_setup_btn_text">Setup</string>
@@@ -84,7 -85,9 +84,7 @@@
      <string name="filedetails_sync_file">Synchronize</string>
      <string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
      <string name="list_layout">List Layout</string>
 -    <string name="action_share_file">Share link</string>
 -    <string name="action_unshare_file">Unshare link</string>
 -    <string name="action_share_with_users">Share with users</string>
 +    <string name="action_share">Share</string>
      <string name="common_yes">Yes</string>
      <string name="common_no">No</string>
      <string name="common_ok">OK</string>
      <string name="unfavorite">Unfavorite</string>
      <string name="common_rename">Rename</string>
      <string name="common_remove">Remove</string>
 -    <string name="confirmation_remove_alert">"Do you really want to remove %1$s?"</string>
 +    <string name="confirmation_remove_file_alert">"Do you really want to remove %1$s?"</string>
      <string name="confirmation_remove_folder_alert">"Do you really want to remove %1$s and its contents?"</string>
      <string name="confirmation_remove_local">Local only</string>
      <string name="confirmation_remove_folder_local">Local only</string>
 -    <string name="confirmation_remove_remote">From server</string>
 +    <string name="confirmation_remove_file_remote">From server</string>
      <string name="confirmation_remove_remote_and_local">Remote &amp; local</string>
      <string name="remove_success_msg">"Removal succeeded"</string>
      <string name="remove_fail_msg">"Removal failed"</string>
      <string name="placeholder_filesize">389 KB</string>
      <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
      <string name="placeholder_media_time">12:23:45</string>
 -
 -    <string name="instant_upload_on_wifi">Upload pictures via WiFi only</string>
 -    <string name="instant_video_upload_on_wifi">Upload videos via WiFi only</string>
 +    
 +    <string name="instant_upload_on_wifi">Upload pictures via wifi only</string>
 +    <string name="instant_upload_on_charging">Upload when charging only</string>
 +    <string name="instant_video_upload_on_wifi">Upload videos via wifi only</string>
 +    <string name="instant_video_upload_on_charging">Upload when charging only</string>
      <string name="instant_upload_path">/InstantUpload</string>
      <string name="conflict_title">File conflict</string>
      <string name="conflict_message">Which files do you want to keep? If you select both versions, the local file will have a number added to its name.</string>
      <string name="preview_image_error_unknown_format">This image cannot be shown</string>
  
      <string name="error__upload__local_file_not_copied">%1$s could not be copied to %2$s local folder</string>
 -    <string name="prefs_instant_upload_path_title">Upload Path</string>
 +    <string name="prefs_instant_upload_path_title">Upload path</string>
  
        <string name="share_link_no_support_share_api">Sorry, sharing is not enabled on your server. Please contact your
                administrator.</string>
        <string name="share_link_file_error">An error occurred while trying to share this file or folder</string>
        <string name="unshare_link_file_no_exist">Unable to unshare. Please check whether the file exists</string>
        <string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
 +    <string name="update_link_file_no_exist">Unable to update. Please check whether the file exists </string>
 +    <string name="update_link_file_error">An error occurred while trying to update the shared link</string>
      <string name="share_link_password_title">Enter a password</string>
      <string name="share_link_empty_password">You must enter a password</string>
  
      <string name="forbidden_permissions_delete">to delete this file</string>
      <string name="share_link_forbidden_permissions">to share this file</string>
      <string name="unshare_link_forbidden_permissions">to unshare this file</string>
 +    <string name="update_link_forbidden_permissions">to update this shared link</string>
      <string name="forbidden_permissions_create">to create the file</string>
      <string name="uploader_upload_forbidden_permissions">to upload in this folder</string>
      <string name="downloader_download_file_not_found">The file is no longer available on the server</string>
  
 +    <string name="file_migration_finish_button">Finish</string>
 +    <string name="file_migration_preparing">Preparing for migration...</string>
 +    <string name="file_migration_checking_destination">Checking destination...</string>
 +    <string name="file_migration_saving_accounts_configuration">Saving accounts configuration...</string>
 +    <string name="file_migration_waiting_for_unfinished_sync">Waiting for unfinished synchronizations...</string>
 +    <string name="file_migration_migrating">Moving data...</string>
 +    <string name="file_migration_updating_index">Updating index...</string>
 +    <string name="file_migration_cleaning">Cleaning...</string>
 +    <string name="file_migration_restoring_accounts_configuration">Restoring accounts configuration...</string>
 +    <string name="file_migration_ok_finished">Finished</string>
 +    <string name="file_migration_failed_not_enough_space">ERROR: Not enough space</string>
 +    <string name="file_migration_failed_not_writable">ERROR: File is not writable</string>
 +    <string name="file_migration_failed_not_readable">ERROR: File is not readable</string>
 +    <string name="file_migration_failed_dir_already_exists">ERROR: owncloud directory already exists</string>
 +    <string name="file_migration_failed_while_coping">ERROR: While migrating</string>
 +    <string name="file_migration_failed_while_updating_index">ERROR: While updating index</string>
 +
      <string name="prefs_category_accounts">Accounts</string>
      <string name="prefs_add_account">Add account</string>
      <string name="auth_redirect_non_secure_connection_title">Secure connection is redirected through an unsecured route.</string>
      <string name="prefs_category_instant_uploading">Instant Uploads</string>
        <string name="prefs_category_security">Security</string>
  
 -      <string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
 +      <string name="prefs_instant_video_upload_path_title">Upload video path</string>
 +    <string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
      <string name="sync_folder_failed_content">Synchronization of %1$s folder could not be completed</string>
  
        <string name="shared_subject_header">shared</string>
      <string name="file_list__footer__files">%1$d files</string>
      <string name="file_list__footer__files_and_folder">%1$d files, 1 folder</string>
      <string name="file_list__footer__files_and_folders">%1$d files, %2$d folders</string>
 +    <string name="action_switch_grid_view">Switch to grid view</string>
 +    <string name="action_switch_list_view">Switch to list view</string>
 +    <string name="common_category">Common</string>
 +    <string name="pref_cache_size">Cache size</string>
 +    <string name="prefs_instant_behaviour_dialogTitle">Upload file to server and ...</string>
 +    <string name="prefs_instant_behaviour_title">Behaviour</string>
+     <string name="prefs_instant_behaviour_dialogTitle">Original file will be...</string>
+     <string name="prefs_instant_behaviour_title">Original file will be...</string>
      <string name="upload_copy_files">Copy file</string>
      <string name="upload_move_files">Move file</string>
 +    <string name="prefs_storage_path">Storage path</string>
 +    <string name="prefs_common">Common</string>
  
 +    <string name="pref_behaviour_entries_do_nothing">do nothing</string>
 +    <string name="pref_behaviour_entries_copy">copy file to OC folder</string>
 +    <string name="pref_behaviour_entries_move">move file to OC folder</string>
 +    <string name="pref_behaviour_entries_delete">delete origin file</string>
 +    <string name="confirmation_remove_files_alert">Do you really want to remove selected items?</string>
 +    <string name="confirmation_remove_folders_alert">Do you really want to remove a folder and its content?</string>
 +    <string name="confirmation_remove_files">selected items</string>
 +    <string name="error_log_exit">Exit</string>
 +    <string name="error_log_send">Send Log</string>
 +    <string name="error_log_title">Error Log</string>
 +    <string name="action_stream_file">Stream file with external player</string>
 +    <string name="stream_expose_password">Do you want to stream this file with an external app?\n\nCAUTION: This may expose your password!</string>
 +    <string name="set_picture_as">Set picture as</string>
 +    <string name="set_as">Set As</string>
+     <string name="pref_behaviour_entries_keep_file">kept in original folder</string>
+     <string name="pref_behaviour_entries_move">moved to ownCloud folder</string>
  
      <string name="share_dialog_title">Sharing</string>
 -    <string name="share_with_user_section_title">Share with Users and Groups</string>
 +    <string name="share_with_user_section_title">Share with users and groups</string>
      <string name="share_no_users">No data shared with users yet</string>
      <string name="share_add_user_or_group">Add User or Group</string>
 +    <string name="share_via_link_section_title">Share link</string>
 +    <string name="share_via_link_expiration_date_label">Set expiration date</string>
 +    <string name="share_via_link_password_label">Password protect</string>
 +    <string name="share_via_link_password_title">Secured</string>
 +    <string name="share_get_public_link_button">Get link</string>
 +
      <string name="share_search">Search</string>
  
      <string name="search_users_and_groups_hint">Search users and groups</string>
  
      <string name="share_sharee_unavailable">Sorry, your server version does not allow share with users within clients.
          \nPlease contact your administrator</string>
 +    <string name="changelog">https://github.com/owncloud/android/raw/beta/CHANGELOG.md</string>
  
  </resources>
diff --combined res/xml/preferences.xml
@@@ -3,7 -3,7 +3,7 @@@
    ownCloud Android client application
  
    Copyright (C) 2012  Bartek Przybylski
 -  Copyright (C) 2015 ownCloud Inc.
 +  Copyright (C) 2012-2013 ownCloud Inc.
  
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2,
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  -->
  <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
 +      <PreferenceCategory android:title="@string/prefs_category_general">
 +              <com.owncloud.android.ui.PreferenceWithLongSummary
 +                      android:title="@string/prefs_storage_path"
 +                      android:key="storage_path" />
 +      </PreferenceCategory>
      <PreferenceCategory android:title="@string/prefs_category_accounts" android:key="accounts_category">
      </PreferenceCategory>
 -    
 +
        <PreferenceCategory android:title="@string/prefs_category_security">
 -          <android.preference.CheckBoxPreference android:title="@string/prefs_passcode" android:key="set_pincode" />
 +              <android.preference.CheckBoxPreference android:title="@string/prefs_passcode" android:key="set_pincode" />
        </PreferenceCategory>
  
      <PreferenceCategory android:title="@string/prefs_category_instant_uploading" android:key="instant_uploading_category">
-          <com.owncloud.android.ui.dialog.OwnCloudListPreference android:key="prefs_instant_behaviour"
-                        android:dialogTitle="@string/prefs_instant_behaviour_dialogTitle"
-                        android:title="@string/prefs_instant_behaviour_title"
-                        android:entries="@array/pref_behaviour_entries"
-                        android:entryValues="@array/pref_behaviour_entryValues"
-                        android:defaultValue="NOTHING"
-                        android:summary="%s"
-                        />
                <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
                                android:title="@string/prefs_instant_upload"
                                android:summary="@string/prefs_instant_upload_summary"/>
                                                        android:title="@string/prefs_instant_upload_path_title"
                                                        android:key="instant_upload_path" />
            <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 -                                              android:title="@string/instant_upload_on_wifi"
 -                                              android:key="instant_upload_on_wifi"/>
 -          <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_video_uploading"
 -                              android:title="@string/prefs_instant_video_upload"
 -                              android:summary="@string/prefs_instant_video_upload_summary" />
 +                      android:dependency="instant_uploading"
 +                      android:disableDependentsState="true"
 +              android:title="@string/instant_upload_on_wifi"
 +              android:key="instant_upload_on_wifi"/>
 +              <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 +                      android:dependency="instant_uploading"
 +                      android:disableDependentsState="true"
 +                      android:title="@string/instant_upload_on_charging"
 +                      android:key="instant_upload_on_charging"/>
 +
 +              <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 +                      android:key="instant_video_uploading"
 +              android:title="@string/prefs_instant_video_upload"
 +              android:summary="@string/prefs_instant_video_upload_summary" />
            <com.owncloud.android.ui.PreferenceWithLongSummary
 -                                                      android:title="@string/prefs_instant_video_upload_path_title"
 -                                                      android:key="instant_video_upload_path" />
 +                      android:dependency="instant_video_uploading"
 +                      android:disableDependentsState="true"
 +                      android:title="@string/prefs_instant_video_upload_path_title"
 +                      android:key="instant_video_upload_path" />
            <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 -                                              android:title="@string/instant_video_upload_on_wifi"
 -                                              android:key="instant_video_upload_on_wifi"/>
 +                      android:dependency="instant_video_uploading"
 +                      android:disableDependentsState="true"
 +              android:title="@string/instant_video_upload_on_wifi"
 +              android:key="instant_video_upload_on_wifi"/>
 +              <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 +                      android:dependency="instant_video_uploading"
 +                      android:disableDependentsState="true"
 +                      android:title="@string/instant_video_upload_on_charging"
 +                      android:key="instant_video_upload_on_charging"/>
+               <com.owncloud.android.ui.dialog.OwnCloudListPreference android:key="prefs_instant_behaviour"
+                       android:dialogTitle="@string/prefs_instant_behaviour_dialogTitle"
+                       android:title="@string/prefs_instant_behaviour_title"
+                       android:entries="@array/pref_behaviour_entries"
+                       android:entryValues="@array/pref_behaviour_entryValues"
+                       android:defaultValue="NOTHING"
+                       android:summary="%s"
+                       />
 -          <!-- DISABLED FOR RELEASE UNTIL FIXED
 +      </PreferenceCategory>
 +
 +      <PreferenceCategory android:title="@string/common_category" android:key="common_category">
 +              <EditTextPreference android:title="@string/pref_cache_size"
 +                                                      android:key="pref_cache_size"
 +                                                      android:digits="0123456789"/>
 +      </PreferenceCategory>
 +
 +      <PreferenceCategory android:title="@string/prefs_category_more" android:key="more">
 +              <!-- DISABLED FOR RELEASE UNTIL FIXED
            CheckBoxPreference android:key="log_to_file"
                                android:title="@string/prefs_log_title"
                                android:summary="@string/prefs_log_summary"/>
                <Preference             android:key="log_history"
                                android:title="@string/prefs_log_title_history"
                                android:summary="@string/prefs_log_summary_history"/ -->
 -                        
 -    </PreferenceCategory>
 -      
 -      <PreferenceCategory android:title="@string/prefs_category_more" android:key="more">
      <Preference android:title="@string/prefs_help" android:key="help" />
      <Preference android:title="@string/prefs_recommend" android:key="recommend" />
      <Preference android:title="@string/prefs_feedback" android:key="feedback" />
      <Preference android:title="@string/prefs_imprint" android:key="imprint" />
 -                        
 -      <Preference             android:id="@+id/about_app" 
 -                                      android:title="@string/about_title" 
 +
 +      <Preference             android:id="@+id/about_app"
 +                                      android:title="@string/about_title"
                                        android:key="about_app" />
 -      </PreferenceCategory>
 -    
  
 +      <Preference android:id="@+id/beta_link"
 +                              android:title="Download latest beta version"
 +                              android:key="beta_link" />
 +
 +      <Preference android:id="@+id/changelog_link"
 +              android:title="Changelog beta version"
 +              android:key="changelog_link" />
 +
 +      </PreferenceCategory>
  </PreferenceScreen>
@@@ -29,16 -29,15 +29,16 @@@ import com.owncloud.android.files.servi
  import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.utils.FileStorageUtils;
  
 -
  import android.accounts.Account;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
 +import android.content.IntentFilter;
  import android.content.SharedPreferences;
  import android.database.Cursor;
  import android.net.ConnectivityManager;
  import android.net.NetworkInfo.State;
 +import android.os.BatteryManager;
  import android.preference.PreferenceManager;
  import android.provider.MediaStore.Images;
  import android.provider.MediaStore.Video;
@@@ -60,7 -59,7 +60,7 @@@ public class InstantUploadBroadcastRece
      @Override
      public void onReceive(Context context, Intent intent) {
          Log_OC.d(TAG, "Received: " + intent.getAction());
 -        if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
 +        if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION) || intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
              handleConnectivityAction(context, intent);
          }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
              handleNewPictureAction(context, intent); 
          file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
          mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE));
          c.close();
 -        
          Log_OC.d(TAG, file_path + "");
  
          // save always temporally the picture to upload
          db.putFileForLater(file_path, account.name, null);
          db.close();
  
 -        if (!isOnline(context) || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
 +        if (!isOnline(context) 
 +                || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))
 +                || (instantUploadWhenChargingOnly(context) && !isCharging(context))
 +           ) {
              return;
          }
  
          if (behaviour.equalsIgnoreCase("NOTHING")) {
              Log_OC.d(TAG, "upload file and do nothing");
              i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_FORGET);
-         } else if (behaviour.equalsIgnoreCase("COPY")) {
-             i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_COPY);
-             Log_OC.d(TAG, "upload file and copy file to oc folder");
          } else if (behaviour.equalsIgnoreCase("MOVE")) {
              i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
              Log_OC.d(TAG, "upload file and move file to oc folder");
-         } else if (behaviour.equalsIgnoreCase("DELETE")){
-             i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_REMOVE);
-             Log_OC.d(TAG, "upload file and delete file in original place");
          }
          return i;
      }
  
          mime_type = c.getString(c.getColumnIndex(Video.Media.MIME_TYPE));
          c.close();
          Log_OC.d(TAG, file_path + "");
 +        
 +        // save always temporally the picture to upload
 +        DbHandler db = new DbHandler(context);
 +        db.putFileForLater(file_path, account.name, null);
 +        db.close();
  
 -        if (!isOnline(context) || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
 +        if (!isOnline(context) 
 +                || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))
 +                || (instantVideoUploadWhenChargingOnly(context) && !isCharging(context))
 +           ) {
              return;
          }
  
      }
  
      private void handleConnectivityAction(Context context, Intent intent) {
 -        if (!instantPictureUploadEnabled(context)) {
 +        if (!instantPictureUploadEnabled(context) && !instantVideoUploadEnabled(context)) {
              Log_OC.d(TAG, "Instant upload disabled, don't upload anything");
              return;
          }
  
 +        if (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context)){
 +            Account account = AccountUtils.getCurrentOwnCloudAccount(context);
 +            if (account == null) {
 +                Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
 +                return;
 +            }
 +
 +            Intent i = new Intent(context, FileUploader.class);
 +            i.putExtra(FileUploader.KEY_ACCOUNT, account);
 +            i.putExtra(FileUploader.KEY_CANCEL_ALL, true);
 +            context.startService(i);
 +        }
 +
          if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
                  && isOnline(context)
 -                && (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
 +                && (!instantUploadWhenChargingOnly(context) || (instantUploadWhenChargingOnly(context) && isCharging(context)))
 +                && (!instantVideoUploadWhenChargingOnly(context) || (instantVideoUploadWhenChargingOnly(context) && isCharging(context)))
 +                && (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) && isConnectedViaWiFi(context)))
 +                && (!instantVideoUploadViaWiFiOnly(context) || (instantVideoUploadViaWiFiOnly(context) && isConnectedViaWiFi(context)))
 +            ) {
              DbHandler db = new DbHandler(context);
              Cursor c = db.getAwaitingFiles();
              if (c.moveToFirst()) {
                  do {
 +                    if (instantPictureUploadViaWiFiOnly(context) &&
 +                            !isConnectedViaWiFi(context)){
 +                        break;
 +                    }
 +
                      String account_name = c.getString(c.getColumnIndex("account"));
                      String file_path = c.getString(c.getColumnIndex("path"));
                      File f = new File(file_path);
              c.close();
              db.close();
          }
 -
      }
  
      public static boolean isOnline(Context context) {
                  && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
                  && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
      }
 +    
 +    public static boolean isCharging(Context context){
 +        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
 +        Intent batteryStatus = context.registerReceiver(null, ifilter);
 +
 +        int status = 0;
 +        if (batteryStatus != null) {
 +            status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
 +        }
 +        return status == BatteryManager.BATTERY_STATUS_CHARGING ||
 +                status == BatteryManager.BATTERY_STATUS_FULL;
 +    }
  
      public static boolean instantPictureUploadEnabled(Context context) {
          return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
      public static boolean instantVideoUploadViaWiFiOnly(Context context) {
          return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_wifi", false);
      }
 +    public static boolean instantUploadWhenChargingOnly(Context context) {
 +        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_charging", false);
 +    }
 +    public static boolean instantVideoUploadWhenChargingOnly(Context context) {
 +        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_charging", false);
 +    }
  }
@@@ -98,12 -98,9 +98,11 @@@ public class FileUploader extends Servi
      public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD";
      public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
  
 +    public static final String KEY_CANCEL_ALL = "CANCEL_ALL";
 +
      public static final int LOCAL_BEHAVIOUR_COPY = 0;
      public static final int LOCAL_BEHAVIOUR_MOVE = 1;
      public static final int LOCAL_BEHAVIOUR_FORGET = 2;
-     public static final int LOCAL_BEHAVIOUR_REMOVE = 3;
  
      public static final int UPLOAD_SINGLE_FILE = 0;
      public static final int UPLOAD_MULTIPLE_FILES = 1;
      public int onStartCommand(Intent intent, int flags, int startId) {
          Log_OC.d(TAG, "Starting command with id " + startId);
  
 +        if (intent.hasExtra(KEY_CANCEL_ALL) && intent.hasExtra(KEY_ACCOUNT)){
 +            Account account = intent.getParcelableExtra(KEY_ACCOUNT);
 +
 +            Log_OC.d(TAG, "Account= " + account.name);
 +
 +            if (mCurrentUpload != null) {
 +                Log_OC.d(TAG, "Current Upload Account= " + mCurrentUpload.getAccount().name);
 +                if (mCurrentUpload.getAccount().name.equals(account.name)) {
 +                    mCurrentUpload.cancel();
 +                }
 +            }
 +            // Cancel pending uploads
 +            cancelUploadsForAccount(account);
 +        }
 +
          if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)
                  || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
              Log_OC.e(TAG, "Not enough information provided in intent");
  
          boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
          boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
-         int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
+         int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_FORGET);
  
          if (intent.hasExtra(KEY_FILE) && files == null) {
              Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");
                  Pair<String, String> putResult = mPendingUploads.putIfAbsent(
                          account, files[i].getRemotePath(), newUpload
                  );
-                 uploadKey = putResult.first;
-                 requestedUploads.add(uploadKey);
+                 if (putResult != null) {
+                     uploadKey = putResult.first;
+                     requestedUploads.add(uploadKey);
+                 }   // else, file already in the queue of uploads; don't repeat the request
              }
  
          } catch (IllegalArgumentException e) {
@@@ -332,10 -332,7 +332,7 @@@ public class UploadFileOperation extend
              // location in the ownCloud local folder
              if (result.isSuccess()) {
                  if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
-                     mFile.setStoragePath(null);
-                 } else if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_REMOVE){
-                     mFile.setStoragePath(null);
-                     originalFile.delete();
+                     mFile.setStoragePath("");
                  } else {
                      mFile.setStoragePath(expectedPath);
                      File fileToMove = null;
              if (temporalFile != null && !originalFile.equals(temporalFile)) {
                  temporalFile.delete();
              }
 +            if (result == null){
 +                return new RemoteOperationResult(false, 404, null);
 +            }
              if (result.isSuccess()) {
                  Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " +
                          result.getLogMessage());
@@@ -26,7 -26,6 +26,7 @@@ import android.accounts.Account
  import android.accounts.AccountManager;
  import android.accounts.AuthenticatorException;
  import android.annotation.TargetApi;
 +import android.os.Parcelable;
  import android.support.v7.app.AlertDialog;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
@@@ -51,7 -50,6 +51,7 @@@ import android.support.v4.app.FragmentM
  import android.support.v4.app.FragmentTransaction;
  import android.support.v4.content.ContextCompat;
  import android.support.v4.view.GravityCompat;
 +import android.support.v7.app.AlertDialog;
  import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
@@@ -81,11 -79,14 +81,11 @@@ import com.owncloud.android.lib.common.
  import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.operations.CopyFileOperation;
  import com.owncloud.android.operations.CreateFolderOperation;
 -import com.owncloud.android.operations.CreateShareViaLinkOperation;
 -import com.owncloud.android.operations.CreateShareWithShareeOperation;
  import com.owncloud.android.operations.MoveFileOperation;
  import com.owncloud.android.operations.RefreshFolderOperation;
  import com.owncloud.android.operations.RemoveFileOperation;
  import com.owncloud.android.operations.RenameFileOperation;
  import com.owncloud.android.operations.SynchronizeFileOperation;
 -import com.owncloud.android.operations.UnshareOperation;
  import com.owncloud.android.services.observer.FileObserverService;
  import com.owncloud.android.syncadapter.FileSyncAdapter;
  import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
@@@ -107,8 -108,6 +107,8 @@@ import com.owncloud.android.utils.FileS
  import com.owncloud.android.utils.UriUtils;
  
  import java.io.File;
 +import java.util.ArrayList;
 +import java.util.Iterator;
  
  /**
   * Displays, what files the user has available in his ownCloud.
@@@ -149,14 -148,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) {
              /// First fragment
              OCFileListFragment listOfFiles = getListOfFilesFragment();
              if (listOfFiles != null) {
 -                listOfFiles.listDirectory(getCurrentDir());
 -                // TODO Enable when "On Device" is recovered
 -                // listOfFiles.listDirectory(getCurrentDir(), MainApp.getOnlyOnDevice());
 -
 +                listOfFiles.listDirectory(getCurrentDir(), MainApp.getOnlyOnDevice());
              } else {
                  Log_OC.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
              }
                      startTextPreview(file);
              }
  
 +            switchLayout(getFile());
 +
          } else {
              Log_OC.wtf(TAG, "initFragments() called with invalid NULLs!");
              if (getAccount() == null) {
          }
      }
  
 +    private void switchLayout(OCFile file){
 +        if (DisplayUtils.isGridView(file, getStorageManager())){
 +            switchToGridView();
 +        } else {
 +            switchToListView();
 +        }
 +    }
 +
      private Fragment chooseInitialSecondFragment(OCFile file) {
          Fragment secondFragment = null;
          if (file != null && !file.isFolder()) {
      protected void refreshListOfFilesFragment() {
          OCFileListFragment fileListFragment = getListOfFilesFragment();
          if (fileListFragment != null) {
 -            fileListFragment.listDirectory();
 -            // TODO Enable when "On Device" is recovered ?
 -            // fileListFragment.listDirectory(MainApp.getOnlyOnDevice());
 +            fileListFragment.listDirectory(MainApp.getOnlyOnDevice());
          }
      }
  
      @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) {
       */
      private void requestMoveOperation(Intent data, int resultCode) {
          OCFile folderToMoveAt = (OCFile) data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
 -        OCFile targetFile = (OCFile) data.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
 -        getFileOperationsHelper().moveFile(folderToMoveAt, targetFile);
 +
 +        ArrayList<OCFile> files = data.getParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES);
 +
 +        for (Parcelable file : files) {
 +            getFileOperationsHelper().moveFile(folderToMoveAt, (OCFile) file);
 +        }
      }
  
      /**
       */
      private void requestCopyOperation(Intent data, int resultCode) {
          OCFile folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
 -        OCFile targetFile = data.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
 -        getFileOperationsHelper().copyFile(folderToMoveAt, targetFile);
 +
 +        ArrayList<OCFile> files = data.getParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES);
 +
 +        for (Parcelable file : files) {
 +            getFileOperationsHelper().copyFile(folderToMoveAt, (OCFile) file);
 +        }
      }
  
      @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();
 +        }
 +    }
 +
 +    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 {
 -            super.onBackPressed();
 +            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 {
  
                                      currentDir.getRemotePath().equals(synchFolderRemotePath)) {
                                  OCFileListFragment fileListFragment = getListOfFilesFragment();
                                  if (fileListFragment != null) {
 -                                    fileListFragment.listDirectory();
 -                                    // TODO Enable when "On Device" is recovered ?
 -                                    // fileListFragment.listDirectory(currentDir,
 -                                    // MainApp.getOnlyOnDevice());
 +                                    fileListFragment.listDirectory(currentDir,
 +                                    MainApp.getOnlyOnDevice());
                                  }
                              }
                              setFile(currentFile);
          @Override
          public void onReceive(Context context, Intent intent) {
              try {
-                 String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+                 String uploadedRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
                  String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
                  boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name);
                  OCFile currentDir = getCurrentDir();
  
                  if (sameAccount && isDescendant) {
                      String linkedToRemotePath =
-                             intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
+                             intent.getStringExtra(FileUploader.EXTRA_LINKED_TO_PATH);
                      if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
                          refreshListOfFilesFragment();
                      }
          OCFileListFragment listOfFiles = getListOfFilesFragment();
          if (listOfFiles != null) {  // should never be null, indeed
              OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
 -            listOfFiles.listDirectory(root);
 -            // TODO Enable when "On Device" is recovered ?
 -            // listOfFiles.listDirectory(root, MainApp.getOnlyOnDevice());
 +            listOfFiles.listDirectory(root, MainApp.getOnlyOnDevice());
              setFile(listOfFiles.getCurrentFile());
              startSyncFolderOperation(root, false);
          }
          cleanSecondFragment();
          // Sync Folder
          startSyncFolderOperation(directory, false);
 +
 +        MenuItem menuItem = mOptionsMenu.findItem(R.id.action_switch_view);
 +
 +        changeGridIcon();
 +        switchLayout(directory);
      }
  
      /**
              // getFileDownloadBinder() - THIS IS A MESS
              OCFileListFragment listOfFiles = getListOfFilesFragment();
              if (listOfFiles != null) {
 -                listOfFiles.listDirectory();
 -                // TODO Enable when "On Device" is recovered ?
 -                // listOfFiles.listDirectory(MainApp.getOnlyOnDevice());
 +                listOfFiles.listDirectory(MainApp.getOnlyOnDevice());
              }
              FileFragment secondFragment = getSecondFragment();
              if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
          } else if (operation instanceof CreateFolderOperation) {
              onCreateFolderOperationFinish((CreateFolderOperation) operation, result);
  
 -        } else if (operation instanceof CreateShareViaLinkOperation ||
 -                    operation instanceof CreateShareWithShareeOperation ) {
 -
 -            refreshShowDetails();
 -            refreshListOfFilesFragment();
 -
 -        } else if (operation instanceof UnshareOperation) {
 -            onUnshareLinkOperationFinish((UnshareOperation) operation, result);
 -
          } else if (operation instanceof MoveFileOperation) {
              onMoveFileOperationFinish((MoveFileOperation) operation, result);
  
  
      }
  
 -    private void onUnshareLinkOperationFinish(UnshareOperation operation,
 -                                              RemoteOperationResult result) {
 -        if (result.isSuccess()) {
 -            refreshShowDetails();
 -            refreshListOfFilesFragment();
 -
 -        } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
 -            cleanSecondFragment();
 -            refreshListOfFilesFragment();
 -        }
 -    }
 -
      private void refreshShowDetails() {
          FileFragment details = getSecondFragment();
          if (details != null) {
      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();
     }
 +
 +    public void refreshDirectory(){
 +        getListOfFilesFragment().refreshDirectory();
 +    }
  }
@@@ -34,9 -34,7 +34,9 @@@ import android.content.pm.PackageInfo
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.content.res.Configuration;
  import android.net.Uri;
 +import android.os.AsyncTask;
  import android.os.Bundle;
 +import android.os.Environment;
  import android.os.Handler;
  import android.os.IBinder;
  import android.preference.CheckBoxPreference;
@@@ -64,7 -62,6 +64,7 @@@ import android.widget.AdapterView.OnIte
  import android.widget.ArrayAdapter;
  import android.widget.ListAdapter;
  import android.widget.ListView;
 +import android.widget.Toast;
  
  import com.owncloud.android.BuildConfig;
  import com.owncloud.android.MainApp;
@@@ -73,25 -70,15 +73,25 @@@ import com.owncloud.android.authenticat
  import com.owncloud.android.authentication.AuthenticatorActivity;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.datamodel.ThumbnailsCacheManager;
  import com.owncloud.android.db.DbHandler;
  import com.owncloud.android.files.FileOperationsHelper;
  import com.owncloud.android.files.services.FileDownloader;
  import com.owncloud.android.files.services.FileUploader;
  import com.owncloud.android.lib.common.utils.Log_OC;
  import com.owncloud.android.services.OperationsService;
 +import com.owncloud.android.ui.PreferenceWithLongSummary;
  import com.owncloud.android.ui.RadioButtonPreference;
  import com.owncloud.android.utils.DisplayUtils;
  
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.io.InputStreamReader;
 +import java.net.MalformedURLException;
 +import java.net.URL;
 +import java.util.concurrent.ExecutionException;
 +import java.io.File;
 +
  
  /**
   * An Activity that allows the user to change the application's settings.
@@@ -106,10 -93,6 +106,10 @@@ public class Preferences extends Prefer
  
      private static final int ACTION_SELECT_UPLOAD_PATH = 1;
      private static final int ACTION_SELECT_UPLOAD_VIDEO_PATH = 2;
 +    private static final int ACTION_REQUEST_PASSCODE = 5;
 +    private static final int ACTION_CONFIRM_PASSCODE = 6;
 +    private static final int ACTION_SELECT_STORAGE_PATH = 3;
 +    private static final int ACTION_PERFORM_MIGRATION = 4;
  
      private DbHandler mDbHandler;
      private CheckBoxPreference pCode;
      private String mUploadPath;
      private PreferenceCategory mPrefInstantUploadCategory;
      private Preference mPrefInstantUpload;
+     private Preference mPrefInstantUploadBehaviour;
      private Preference mPrefInstantUploadPath;
      private Preference mPrefInstantUploadPathWiFi;
      private Preference mPrefInstantVideoUpload;
      protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
      protected FileUploader.FileUploaderBinder mUploaderBinder = null;
      private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
 +    private PreferenceWithLongSummary mPrefStoragePath;
 +    private String mStoragePath;
 +
  
      @SuppressWarnings("deprecation")
      @Override
          registerForContextMenu(getListView());
  
          pCode = (CheckBoxPreference) findPreference("set_pincode");
 -        if (pCode != null){
 +        if (pCode != null) {
              pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
                  @Override
                  public boolean onPreferenceChange(Preference preference, Object newValue) {
                      Intent i = new Intent(getApplicationContext(), PassCodeActivity.class);
 -                    Boolean enable = (Boolean) newValue;
 +                    Boolean incoming = (Boolean) newValue;
 +
                      i.setAction(
 -                            enable.booleanValue() ? PassCodeActivity.ACTION_ENABLE :
 -                                    PassCodeActivity.ACTION_DISABLE
 +                            incoming.booleanValue() ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT :
 +                                    PassCodeActivity.ACTION_CHECK_WITH_RESULT
                      );
 -                    startActivity(i);
 -                    
 -                    return true;
 +
 +                    startActivityForResult(i, incoming.booleanValue() ? ACTION_REQUEST_PASSCODE :
 +                            ACTION_CONFIRM_PASSCODE);
 +
 +                    // Don't update just yet, we will decide on it in onActivityResult
 +                    return false;
                  }
 -            });            
 +            });
              
          }
  
 +        final Preference pCacheSize = findPreference("pref_cache_size");
 +        if (pCacheSize != null){
 +            final SharedPreferences appPrefs =
 +                    PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 +            Long cacheSize = ThumbnailsCacheManager.getMaxSize();
 +            pCacheSize.setSummary(cacheSize + " Mb");
 +            pCacheSize.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
 +                @Override
 +                public boolean onPreferenceChange(Preference preference, Object newValue) {
 +                    int size = Integer.decode((String) newValue);
 +                    if (ThumbnailsCacheManager.setMaxSize(size)){
 +                        appPrefs.edit().putInt("pref_cache_size", size);
 +                        pCacheSize.setSummary(size + " MB");
 +                        return true;
 +                    } else {
 +                        return false;
 +                    }
 +                }
 +            });
 +        }
 +
          PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
          
          boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
 -        Preference pHelp =  findPreference("help");
 +        Preference pHelp = findPreference("help");
          if (pHelp != null ){
              if (helpEnabled) {
                  pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
          }
  
          if (BuildConfig.DEBUG) {
 -            Preference pLog =  findPreference("log");
 +            Preference pLog = findPreference("log");
              if (pLog != null ){
                  pLog.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                      @Override
                          intent.putExtra(Intent.EXTRA_TEXT, recommendText);
                          startActivity(intent);
  
 -                        return(true);
 +                        return true;
  
                      }
                  });
                  pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                      @Override
                      public boolean onPreferenceClick(Preference preference) {
 -                        String feedbackMail   =(String) getText(R.string.mail_feedback);
 -                        String feedback   =(String) getText(R.string.prefs_feedback) + " - android v" + appVersion;
 -                        Intent intent = new Intent(Intent.ACTION_SENDTO); 
 +                        String feedbackMail = (String) getText(R.string.mail_feedback);
 +                        String feedback     = String.format("%s - android v%s", getText(R.string.prefs_feedback),  appVersion);
 +                        Intent intent       = new Intent(Intent.ACTION_SENDTO);
 +
                          intent.setType("text/plain");
                          intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
                          
              }
          }
  
 -        mPrefInstantUploadPath =  findPreference("instant_upload_path");
 +        mPrefStoragePath =  (PreferenceWithLongSummary)findPreference("storage_path");
 +        if (mPrefStoragePath != null) {
 +
 +            mPrefStoragePath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
 +                @Override
 +                public boolean onPreferenceClick(Preference preference) {
 +                    Intent intent = new Intent(Preferences.this, LocalDirectorySelectorActivity.class);
 +                    intent.putExtra(UploadFilesActivity.KEY_DIRECTORY_PATH, mStoragePath);
 +                    startActivityForResult(intent, ACTION_SELECT_STORAGE_PATH);
 +                    return true;
 +                }
 +            });
 +
 +            mPrefStoragePath.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
 +                    @Override
 +                    public boolean onPreferenceChange(Preference preference, Object newValue) {
 +                        MainApp.setStoragePath((String) newValue);
 +                        return true;
 +                    }
 +                });
 +        }
 +
 +        mPrefInstantUploadPath = (PreferenceWithLongSummary)findPreference("instant_upload_path");
          if (mPrefInstantUploadPath != null){
  
              mPrefInstantUploadPath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
          mPrefInstantUploadCategory =
                  (PreferenceCategory) findPreference("instant_uploading_category");
          
 -        mPrefInstantUploadPathWiFi =  findPreference("instant_upload_on_wifi");
 +        mPrefInstantUploadPathWiFi = findPreference("instant_upload_on_wifi");
          mPrefInstantUpload = findPreference("instant_uploading");
          
          toggleInstantPictureOptions(((CheckBoxPreference) mPrefInstantUpload).isChecked());
              @Override
              public boolean onPreferenceChange(Preference preference, Object newValue) {
                  toggleInstantPictureOptions((Boolean) newValue);
+                 toggleInstantUploadBehaviour(
+                         ((CheckBoxPreference)mPrefInstantVideoUpload).isChecked(),
+                         (Boolean) newValue);
                  return true;
              }
          });
          toggleInstantVideoOptions(((CheckBoxPreference) mPrefInstantVideoUpload).isChecked());
          
          mPrefInstantVideoUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-             
              @Override
              public boolean onPreferenceChange(Preference preference, Object newValue) {
                  toggleInstantVideoOptions((Boolean) newValue);
+                 toggleInstantUploadBehaviour(
+                         (Boolean) newValue,
+                         ((CheckBoxPreference) mPrefInstantUpload).isChecked());
                  return true;
              }
          });
-             
+         mPrefInstantUploadBehaviour = findPreference("prefs_instant_behaviour");
+         toggleInstantUploadBehaviour(
+                 ((CheckBoxPreference)mPrefInstantVideoUpload).isChecked(),
+                 ((CheckBoxPreference)mPrefInstantUpload).isChecked());
          /* About App */
 -       pAboutApp = (Preference) findPreference("about_app");
 +       pAboutApp = findPreference("about_app");
         if (pAboutApp != null) { 
 -               pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
 -               pAboutApp.setSummary(String.format(getString(R.string.about_version), appVersion));
 +               pAboutApp.setTitle(String.format(getString(R.string.about_android),
 +                                                getString(R.string.app_name)));
 +           try {
 +               Integer currentVersion = getPackageManager().getPackageInfo
 +                  (getPackageName(), 0).versionCode;
 +               pAboutApp.setSummary(String.format(getString(R.string.about_version),
 +                                    currentVersion));
 +           } catch (NameNotFoundException e) {
 +           }
         }
  
         loadInstantUploadPath();
 +       loadStoragePath();
         loadInstantUploadVideoPath();
  
          /* ComponentsGetter */
                      Context.BIND_AUTO_CREATE);
          }
  
 +        /* Link to Beta apks */
 +        Preference pBetaLink =  findPreference("beta_link");
 +        if (pBetaLink != null ){
 +            pBetaLink.setOnPreferenceClickListener(new OnPreferenceClickListener() {
 +                @Override
 +                public boolean onPreferenceClick(Preference preference) {
 +                    Integer latestVersion = -1;
 +                    Integer currentVersion = -1;
 +                    try {
 +                        currentVersion = getPackageManager().getPackageInfo
 +                                                 (getPackageName(), 0).versionCode;
 +                        LoadingVersionNumberTask loadTask = new LoadingVersionNumberTask();
 +                        loadTask.execute();
 +                        latestVersion = loadTask.get();
 +                    } catch (InterruptedException | ExecutionException e) {
 +                        e.printStackTrace();
 +                    } catch (NameNotFoundException e) {
 +                        e.printStackTrace();
 +                    }
 +                    if (latestVersion == -1 || currentVersion == -1) {
 +                        Toast.makeText(getApplicationContext(), "No information available!",
 +                                       Toast.LENGTH_SHORT).show();
 +                    }
 +                    if (latestVersion > currentVersion) {
 +                        String betaLinkWeb = (String) getText(R.string.beta_link) +
 +                                                              latestVersion + ".apk";
 +                        if (betaLinkWeb != null && betaLinkWeb.length() > 0) {
 +                            Uri uriUrl = Uri.parse(betaLinkWeb);
 +                            Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
 +                            startActivity(intent);
 +                            return true;
 +                        }
 +                    } else {
 +                        Toast.makeText(getApplicationContext(), "No new version available!",
 +                                       Toast.LENGTH_SHORT).show();
 +                        return true;
 +                    }
 +                    return true;
 +                }
 +            });
 +        }
 +
 +        /* Link to Beta apks */
 +        Preference pChangelogLink =  findPreference("changelog_link");
 +        if (pChangelogLink != null) {
 +            pChangelogLink.setOnPreferenceClickListener(new OnPreferenceClickListener() {
 +                @Override
 +                public boolean onPreferenceClick(Preference preference) {
 +                    String betaLinkWeb = getString(R.string.changelog);
 +                    if (betaLinkWeb != null && betaLinkWeb.length() > 0) {
 +                        Uri uriUrl = Uri.parse(betaLinkWeb);
 +                        Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
 +                        startActivity(intent);
 +                        return true;
 +                    }
 +                    return true;
 +                }
 +            });
 +        }
      }
      
      private void toggleInstantPictureOptions(Boolean value){
              mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPathWiFi);
              mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPath);
          } else {
 -            mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi);
 -            mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath);
 +//            mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi);
 +//            mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath);
          }
      }
      
              mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPathWiFi);
              mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPath);
          } else {
 -            mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi);
 -            mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath);
 +//            mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi);
 +//            mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath);
          }
      }
  
+     private void toggleInstantUploadBehaviour(Boolean video, Boolean picture){
+         if (picture || video){
+             mPrefInstantUploadCategory.addPreference(mPrefInstantUploadBehaviour);
+         } else {
+             mPrefInstantUploadCategory.removePreference(mPrefInstantUploadBehaviour);
+         }
+     }
      @Override
      public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
  
  
          if (requestCode == ACTION_SELECT_UPLOAD_PATH && resultCode == RESULT_OK){
  
 -            OCFile folderToUpload =
 -                    (OCFile) data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
 +            OCFile folderToUpload =  data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
  
              mUploadPath = folderToUpload.getRemotePath();
  
  
              saveInstantUploadPathOnPreferences();
  
 -        } else if (requestCode == ACTION_SELECT_UPLOAD_VIDEO_PATH && resultCode == RESULT_OK){
 +        } else if (requestCode == ACTION_SELECT_UPLOAD_VIDEO_PATH && resultCode == RESULT_OK) {
  
 -            OCFile folderToUploadVideo =
 -                    (OCFile) data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
 +            OCFile folderToUploadVideo = data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
  
              mUploadVideoPath = folderToUploadVideo.getRemotePath();
  
              mPrefInstantVideoUploadPath.setSummary(mUploadVideoPath);
  
              saveInstantUploadVideoPathOnPreferences();
 +        } else if (requestCode == ACTION_SELECT_STORAGE_PATH && resultCode == RESULT_OK) {
 +            File currentStorageDir = new File(mStoragePath);
 +            File upcomingStorageDir = new File(data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES));
 +
 +            if (currentStorageDir != upcomingStorageDir) {
 +                Intent migrationIntent = new Intent(this, StorageMigrationActivity.class);
 +                migrationIntent.putExtra(StorageMigrationActivity.KEY_MIGRATION_SOURCE_DIR,
 +                        currentStorageDir.getAbsolutePath());
 +                migrationIntent.putExtra(StorageMigrationActivity.KEY_MIGRATION_TARGET_DIR,
 +                        upcomingStorageDir.getAbsolutePath());
 +                startActivityForResult(migrationIntent, ACTION_PERFORM_MIGRATION);
 +            }
 +        } else if (requestCode == ACTION_PERFORM_MIGRATION && resultCode == RESULT_OK) {
 +            String resultStorageDir = data.getStringExtra(StorageMigrationActivity.KEY_MIGRATION_TARGET_DIR);
 +            saveStoragePath(resultStorageDir);
 +        } else if (requestCode == ACTION_REQUEST_PASSCODE && resultCode == RESULT_OK) {
 +            String passcode = data.getStringExtra(PassCodeActivity.KEY_PASSCODE);
 +            if (passcode != null && passcode.length() == 4) {
 +                SharedPreferences.Editor appPrefs = PreferenceManager
 +                        .getDefaultSharedPreferences(getApplicationContext()).edit();
 +
 +                for (int i = 1; i <= 4; ++i) {
 +                    appPrefs.putString("PrefPinCode" + i, passcode.substring(i-1, i));
 +                }
 +                appPrefs.putBoolean("set_pincode", true);
 +                appPrefs.commit();
 +                Toast.makeText(this, R.string.pass_code_stored, Toast.LENGTH_LONG).show();
 +            }
 +        } else if (requestCode == ACTION_CONFIRM_PASSCODE && resultCode == RESULT_OK) {
 +            if (data.getBooleanExtra(PassCodeActivity.KEY_CHECK_RESULT, false)) {
 +
 +                SharedPreferences.Editor appPrefs = PreferenceManager
 +                        .getDefaultSharedPreferences(getApplicationContext()).edit();
 +                appPrefs.putBoolean("set_pincode", false);
 +                appPrefs.commit();
 +
 +                Toast.makeText(this, R.string.pass_code_removed, Toast.LENGTH_LONG).show();
 +            }
          }
      }
  
      public void setContentView(View view) {
          getDelegate().setContentView(view);
      }
 +
      @Override
      public void setContentView(View view, ViewGroup.LayoutParams params) {
          getDelegate().setContentView(view, params);
      }
  
      /**
 +     * Save storage path
 +     */
 +    private void saveStoragePath(String newStoragePath) {
 +        SharedPreferences appPrefs =
 +                PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 +        mStoragePath = newStoragePath;
 +        MainApp.setStoragePath(mStoragePath);
 +        SharedPreferences.Editor editor = appPrefs.edit();
 +        editor.putString("storage_path", mStoragePath);
 +        editor.commit();
 +        mPrefStoragePath.setSummary(mStoragePath);
 +    }
 +
 +    /**
 +     * Load storage path set on preferences
 +     */
 +    private void loadStoragePath() {
 +        SharedPreferences appPrefs =
 +                PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 +        mStoragePath = appPrefs.getString("storage_path", Environment.getExternalStorageDirectory()
 +                                                         .getAbsolutePath());
 +        mPrefStoragePath.setSummary(mStoragePath);
 +    }
 +
 +    /**
       * Save the "Instant Upload Path" on preferences
       */
      private void saveInstantUploadPathOnPreferences() {
       * Load upload video path set on preferences
       */
      private void loadInstantUploadVideoPath() {
 -        SharedPreferences appPrefs =
 -                PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 -        mUploadVideoPath = appPrefs.getString("instant_video_upload_path", getString(R.string.instant_upload_path));
 -        mPrefInstantVideoUploadPath.setSummary(mUploadVideoPath);
 +        mPrefInstantVideoUploadPath.setSummary(MainApp.getStoragePath());
      }
  
      /**
          editor.commit();
      }
  
 -    // Methods for ComponetsGetter
 +    // Methods for ComponentsGetter
      @Override
      public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
          return mDownloaderBinder;
  
              if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
                  mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
 -
              } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
                  Log_OC.d(TAG, "Upload service connected");
                  mUploaderBinder = (FileUploader.FileUploaderBinder) service;
 -            } else {
 -                return;
              }
 -
          }
  
          @Override
              }
          }
      };
 +
 +    /**
 +     *
 +     * Class for loading the version number
 +     *
 +     */
 +    private class LoadingVersionNumberTask extends AsyncTask<Void, Void, Integer> {
 +        protected Integer doInBackground(Void... args) {
 +            try {
 +                URL url = new URL("https://github.com/owncloud/android/raw/beta/apks/latest");
 +                BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
 +
 +                Integer latestVersion = Integer.parseInt(in.readLine());
 +                in.close();
 +
 +                return latestVersion;
 +
 +            } catch (MalformedURLException e) {
 +                e.printStackTrace();
 +            } catch (IOException e) {
 +                e.printStackTrace();
 +            }
 +            return -1;
 +        }
 +    }
  }
diff --combined tests/project.properties
@@@ -11,4 -11,4 +11,4 @@@
  #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
  
  # Project target.
--target=android-22
++target=android-23