<?xml version="1.0" encoding="utf-8"?>\r
-<!-- \r
+<!--\r
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 ownCloud Inc.\r
\r
This program is free software: you can redistribute it and/or modify\r
it under the terms of the GNU General Public License version 2,\r
\r
You should have received a copy of the GNU General Public License\r
along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- -->\r
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+ -->\r
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:id="@+id/drawer_layout"\r
android:layout_width="match_parent"\r
android:layout_height="match_parent"\r
- android:background="@color/background_color"\r
- android:orientation="horizontal"\r
- android:baselineAligned="false"\r
- >\r
-\r
- <FrameLayout \r
- android:layout_width="0dp"\r
- android:layout_height="match_parent"\r
- android:layout_weight="1"\r
- android:id="@+id/left_fragment_container"\r
- />\r
- \r
- <FrameLayout \r
- android:layout_width="0dp"\r
- android:layout_height="match_parent"\r
- android:layout_weight="2"\r
- android:id="@+id/right_fragment_container"\r
- />\r
- \r
- </LinearLayout>
+ android:clickable="true" >\r
+\r
+ <!-- The main content view -->\r
+ <LinearLayout\r
+ xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="match_parent"\r
+ android:background="@color/background_color"\r
+ android:baselineAligned="false"\r
+ android:orientation="horizontal" >\r
+\r
+ \r
+ <FrameLayout\r
+ android:id="@+id/left_fragment_container"\r
+ android:layout_width="0dp"\r
+ android:layout_height="match_parent"\r
+ android:layout_weight="1" />\r
+\r
+ <FrameLayout\r
+ android:id="@+id/right_fragment_container"\r
+ android:layout_width="0dp"\r
+ android:layout_height="match_parent"\r
+ android:layout_weight="2" />\r
+ </LinearLayout>\r
+\r
+ \r
+ <LinearLayout\r
+ xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:id="@+id/left_drawer"\r
+ android:layout_width="240dp"\r
+ android:layout_height="match_parent"\r
+ android:layout_gravity="start"\r
+ android:background="@color/background_color"\r
+ android:baselineAligned="false"\r
+ android:clickable="true"\r
+ android:orientation="vertical">\r
+\r
+ <LinearLayout\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content"\r
+ android:layout_margin="5dp">\r
+\r
+ <ImageView\r
+ android:id="@+id/drawer_userIcon"\r
+ android:layout_width="40dp"\r
+ android:layout_height="40dp"\r
+ android:src="@drawable/abs__ab_solid_dark_holo" />\r
+\r
+ <TextView\r
+ android:id="@+id/drawer_username"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center_vertical"\r
+ android:layout_marginLeft="5dp"\r
+ android:layout_marginStart="5dp"\r
+ android:textAppearance="?android:attr/textAppearanceLarge" />\r
+\r
+ </LinearLayout>\r
+\r
+ <TextView\r
+ android:layout_width="fill_parent"\r
+ android:layout_height="2dip"\r
+ android:background="@color/list_item_lastmod_and_filesize_text" />\r
+\r
+ <ListView\r
+ android:id="@+id/drawer_list"\r
+ android:layout_width="fill_parent"\r
+ android:layout_height="match_parent"\r
+ android:background="@color/background_color"\r
+ android:choiceMode="singleChoice"\r
+ />\r
+</LinearLayout>\r
+</android.support.v4.widget.DrawerLayout>
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
android:layout_width="wrap_content" android:background="#fefefe"
android:gravity="center">
<TextView android:layout_width="fill_parent" android:text="@string/uploader_top_message"
- android:layout_height="wrap_content" android:id="@+id/textView1" android:textColor="@android:color/black"
+ android:layout_height="wrap_content" android:id="@+id/drawer_username" android:textColor="@android:color/black"
android:gravity="center_horizontal"></TextView>
<FrameLayout android:layout_height="fill_parent"
android:layout_width="fill_parent" android:id="@+id/frameLayout1"
- android:layout_below="@+id/textView1" android:layout_above="@+id/linearLayout1">
+ android:layout_below="@+id/drawer_username" android:layout_above="@+id/linearLayout1">
<ListView android:id="@android:id/list" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:divider="@drawable/uploader_list_separator"
android:dividerHeight="1dip"></ListView>
</FrameLayout>
- <LinearLayout android:id="@+id/linearLayout1"
- android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:orientation="vertical">
- <Button android:layout_gravity="bottom" android:layout_height="wrap_content"
- android:layout_width="fill_parent" android:id="@+id/uploader_choose_folder"
- android:text="@string/uploader_btn_upload_text"/>
+
+ <LinearLayout
+ android:id="@+id/linearLayout1"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/uploader_new_folder"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:layout_weight="1"
+ android:text="@string/uploader_btn_new_folder_text" />
+
+ <Button
+ android:id="@+id/uploader_choose_folder"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:layout_weight="1"
+ android:text="@string/uploader_btn_upload_text" />
+
</LinearLayout>
</RelativeLayout>
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
android:layout_gravity="center_vertical|center"
android:layout_margin="4dp"
android:src="@drawable/ic_menu_archive"
- android:id="@+id/drawer_userIcon" />
+ android:id="@+id/thumbnail" />
<TextView
android:text="TextView"
android:layout_width="fill_parent"
- android:id="@+id/textView1"
+ android:id="@+id/drawer_username"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:layout_gravity="center_vertical"
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
android:icon="@drawable/ic_action_upload"
android:orderInCategory="2"
android:showAsAction="always"
- android:title="@string/actionbar_upload"/>
+ 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:showAsAction="always"
- android:title="@string/actionbar_mkdir"/>
+ android:title="@string/actionbar_mkdir"
+ android:contentDescription="@string/actionbar_mkdir"/>
<item
- android:id="@+id/action_sync_account"
- android:icon="@drawable/ic_action_refresh"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_sync"
- android:contentDescription="@string/actionbar_sync"/>
- <item
- android:id="@+id/action_settings"
- android:icon="@drawable/ic_action_settings"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_settings"
- android:contentDescription="@string/actionbar_settings"/>
- <item
- android:id="@+id/action_logger"
- android:icon="@drawable/ic_action_settings"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_logger"
- android:contentDescription="@string/actionbar_logger"/>
- <item
android:id="@+id/action_sort"
- android:icon="@android:drawable/ic_menu_sort_alphabetically"
+ android:icon="@android:drawable/ic_menu_sort_by_size"
android:orderInCategory="2"
- android:showAsAction="never"
+ android:showAsAction="always"
- android:title="@string/actionbar_sort"/>
+ android:title="@string/actionbar_sort"
- android:contentDescription="@string/actionbar_sort"/>
++ android:contentDescription="@string/actionbar_sort"/>/>
<!-- <item android:id="@+id/search" android:title="@string/actionbar_search" android:icon="@drawable/ic_action_search"></item> -->
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<color name="filelist_icon_backgorund">#DDDDDD</color>
<color name="owncloud_blue_bright">#00ddff</color>
<color name="list_item_lastmod_and_filesize_text">#989898</color>
+ <color name="black">#000000</color>
+ <color name="textColor">#303030</color>
+ <color name="list_divider_background">#fff0f0f0</color>
</resources>
<!-- TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item> -->
</string-array>
+ <string-array name="drawer_items">
+ <item>Accounts</item>
+ <item>All Files</item>
+ <item>On device</item>
+ <item>Settings</item>
+ </string-array>
+ <string name="drawer_open">ownCloud</string>
+ <string name="drawer_close">Close</string>
<string name="prefs_category_general">General</string>
<string name="prefs_category_more">More</string>
<string name="prefs_accounts">Accounts</string>
<string name="prefs_manage_accounts">Manage Accounts</string>
- <string name="prefs_pincode">App PIN</string>
- <string name="prefs_pincode_summary">Protect your client</string>
+ <string name="prefs_passcode">Passcode lock</string>
<string name="prefs_instant_upload">Instant picture uploads</string>
<string name="prefs_instant_upload_summary">Instantly upload pictures taken by camera</string>
<string name="prefs_instant_video_upload">Instant video uploads</string>
<string name="sync_string_files">Files</string>
<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_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="foreign_files_local_text">"Local: %1$s"</string>
<string name="foreign_files_remote_text">"Remote: %1$s"</string>
<string name="upload_query_move_foreign_files">There is not space enough to copy the selected files into the %1$s folder. Would you like to move them instead? </string>
- <string name="pincode_enter_pin_code">Please, insert your App PIN</string>
+ <string name="pass_code_enter_pass_code">Please, insert your passcode</string>
- <string name="pincode_configure_your_pin">Enter your App PIN</string>
- <string name="pincode_configure_your_pin_explanation">The PIN will be requested every time the app is started</string>
- <string name="pincode_reenter_your_pincode">Please, reenter your App PIN</string>
- <string name="pincode_remove_your_pincode">Remove your App PIN</string>
- <string name="pincode_mismatch">The App PINs are not the same</string>
- <string name="pincode_wrong">Incorrect App PIN</string>
- <string name="pincode_removed">App PIN removed</string>
- <string name="pincode_stored">App PIN stored</string>
+ <string name="pass_code_configure_your_pass_code">Enter your passcode</string>
+ <string name="pass_code_configure_your_pass_code_explanation">The passcode will be requested every time the app is started</string>
+ <string name="pass_code_reenter_your_pass_code">Please, reenter your passcode</string>
+ <string name="pass_code_remove_your_pass_code">Remove your passcode</string>
+ <string name="pass_code_mismatch">The passcodes are not the same</string>
+ <string name="pass_code_wrong">Incorrect passcode</string>
+ <string name="pass_code_removed">Passcode removed</string>
+ <string name="pass_code_stored">Passcode stored</string>
<string name="media_notif_ticker">"%1$s music player"</string>
<string name="media_state_playing">"%1$s (playing)"</string>
<string name="auth_no_net_conn_title">No network connection</string>
<string name="auth_nossl_plain_ok_title">Secure connection unavailable.</string>
<string name="auth_connection_established">Connection established</string>
- <string name="auth_testing_connection">Testing connection…</string>
+ <string name="auth_testing_connection">Testing connection</string>
<string name="auth_not_configured_title">Malformed server configuration</string>
<string name="auth_account_not_new">An account for the same user and server already exists in the device</string>
<string name="auth_account_not_the_same">The entered user does not match the user of this account</string>
<string name="auth_fail_get_user_name">Your server is not returning a correct user id, please contact an administrator
</string>
<string name="auth_can_not_auth_against_server">Cannot authenticate against this server</string>
+ <string name="auth_account_does_not_exist">Account does not exist in the device yet</string>
<string name="fd_keep_in_sync">Keep file up to date</string>
<string name="common_rename">Rename</string>
<string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
<string name="filedisplay_no_file_selected">No file was selected</string>
<string name="activity_chooser_title">Send link to …</string>
+ <string name="wait_for_tmp_copy_from_private_storage">Copying file from private storage</string>
<string name="oauth_check_onoff">Login with oAuth2</string>
<string name="oauth_login_connection">Connecting to oAuth2 server…</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="share_link_password_title">Enter a password</string>
+ <string name="share_link_empty_password">You must enter a password</string>
<string name="activity_chooser_send_file_title">Send</string>
<string name="prefs_category_security">Security</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="subject_token">%1$s shared \"%2$s\" with you</string>
+ <string name="shared_subject_header">shared</string>
+ <string name="with_you_subject_header">with you</string>
++
+ <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
- <string name="username">Username</string>
+ <string name="auth_refresh_button">Refresh connection</string>
+ <string name="auth_host_address">Server address</string>
+ <string name="common_error_out_memory">Not enough memory</string>
+
++ <string name="username">Username</string>
</resources>
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefs_category_security">
- <!-- ListPreference
- android:key="select_oc_account"
- android:title="@string/prefs_select_oc_account"
- android:summary="@string/prefs_summary_select_oc_account"
- / -->
- <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:title="@string/prefs_pincode" android:key="set_pincode"
- android:summary="@string/prefs_pincode_summary"/>
+ <android.preference.CheckBoxPreference android:title="@string/prefs_passcode" android:key="set_pincode" />
</PreferenceCategory>
- <PreferenceCategory android:title="@string/prefs_category_instant_uploading">
- <com.owncloud.android.ui.PreferenceWithLongSummary
- android:title="@string/prefs_instant_upload_path_title"
- android:key="instant_upload_path" />
- <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
+ <PreferenceCategory android:title="@string/prefs_category_instant_uploading" android:key="instant_uploading_category">
+ <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
android:title="@string/prefs_instant_upload"
android:summary="@string/prefs_instant_upload_summary"/>
- <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:dependency="instant_uploading"
- android:disableDependentsState="true"
+ <com.owncloud.android.ui.PreferenceWithLongSummary
+ 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" />
<com.owncloud.android.ui.PreferenceWithLongSummary
android:title="@string/prefs_instant_video_upload_path_title"
android:key="instant_video_upload_path" />
- <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.CheckBoxPreferenceWithLongTitle android:dependency="instant_video_uploading"
- android:disableDependentsState="true"
+ <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
android:title="@string/instant_video_upload_on_wifi"
android:key="instant_video_upload_on_wifi"/>
<!-- DISABLED FOR RELEASE UNTIL FIXED
<PreferenceCategory android:title="@string/prefs_category_more" android:key="more">
<Preference android:title="@string/prefs_help" android:key="help" />
+ <Preference android:title="@string/actionbar_logger" android:key="log" />
<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" />
- /* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*/
package com.owncloud.android;
+ import android.app.Activity;
import android.app.Application;
import android.content.Context;
+ import android.content.pm.PackageInfo;
+ import android.content.pm.PackageManager;
+ import android.os.Build;
+ import android.os.Bundle;
+ import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
import com.owncloud.android.lib.common.utils.Log_OC;
+
+
/**
* Main Application of the project
*
* Contains methods to build the "static" strings. These strings were before constants in different
* classes
- *
- * @author masensio
- * @author David A. Velasco
*/
public class MainApp extends Application {
-
+
+ private static final String TAG = MainApp.class.getSimpleName();
+
private static final String AUTH_ON = "on";
-
+
@SuppressWarnings("unused")
private static final String POLICY_SINGLE_SESSION_PER_ACCOUNT = "single session per account";
@SuppressWarnings("unused")
private static Context mContext;
+ // TODO better place
+ private static boolean mOnlyOnDevice = false;
+
+
public void onCreate(){
super.onCreate();
MainApp.mContext = getApplicationContext();
boolean isSamlAuth = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
-
- if (isSamlAuth) {
+
+ OwnCloudClientManagerFactory.setUserAgent(getUserAgent());
+ if (isSamlAuth) {
OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT);
-
} else {
OwnCloudClientManagerFactory.setDefaultPolicy(Policy.ALWAYS_NEW_CLIENT);
}
Log_OC.startLogging();
Log_OC.d("Debug", "start logging");
}
+
+ // register global protection with pass code
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ this.registerActivityLifecycleCallbacks( new ActivityLifecycleCallbacks() {
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onCreate(Bundle) starting" );
+ PassCodeManager.getPassCodeManager().onActivityCreated(activity);
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onStart() starting" );
+ PassCodeManager.getPassCodeManager().onActivityStarted(activity);
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onResume() starting" );
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onPause() ending");
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onStop() ending" );
+ PassCodeManager.getPassCodeManager().onActivityStopped(activity);
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onSaveInstanceState(Bundle) starting" );
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ Log_OC.d(activity.getClass().getSimpleName(), "onDestroy() ending" );
+ }
+ });
+ }
}
public static Context getAppContext() {
public static String getLogName() {
return getAppContext().getResources().getString(R.string.log_name);
}
+
+ public static void showOnlyFilesOnDevice(boolean state){
+ mOnlyOnDevice = state;
+ }
+
+ public static boolean getOnlyOnDevice(){
+ return mOnlyOnDevice;
+ }
+
+ // user agent
+ public static String getUserAgent() {
+ String appString = getAppContext().getResources().getString(R.string.user_agent);
+ String packageName = getAppContext().getPackageName();
+ String version = "";
+
+ PackageInfo pInfo = null;
+ try {
+ pInfo = getAppContext().getPackageManager().getPackageInfo(packageName, 0);
+ if (pInfo != null) {
+ version = pInfo.versionName;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log_OC.e(TAG, "Trying to get packageName", e.getCause());
+ }
+
+ // Mozilla/5.0 (Android) ownCloud-android/1.7.0
+ String userAgent = String.format(appString, version);
+
+ return userAgent;
+ }
}
- /* ownCloud Android client application
+ /**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
import java.util.List;
import java.util.Vector;
-import com.owncloud.android.MainApp;
-import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.lib.resources.shares.ShareType;
-import com.owncloud.android.lib.resources.files.FileUtils;
-import com.owncloud.android.utils.FileStorageUtils;
-
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
+ import android.provider.MediaStore;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.FileUtils;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.utils.FileStorageUtils;
+
public class FileDataStorageManager {
public static final int ROOT_PARENT_ID = 0;
}
- public Vector<OCFile> getFolderContent(OCFile f) {
+ public Vector<OCFile> getFolderContent(OCFile f, boolean onlyOnDevice) {
if (f != null && f.isFolder() && f.getFileId() != -1) {
- return getFolderContent(f.getFileId());
+ return getFolderContent(f.getFileId(), onlyOnDevice);
} else {
return new Vector<OCFile>();
}
- public Vector<OCFile> getFolderImages(OCFile folder) {
+ public Vector<OCFile> getFolderImages(OCFile folder, boolean onlyOnDevice) {
Vector<OCFile> ret = new Vector<OCFile>();
if (folder != null) {
// TODO better implementation, filtering in the access to database instead of here
- Vector<OCFile> tmp = getFolderContent(folder);
+ Vector<OCFile> tmp = getFolderContent(folder, onlyOnDevice);
OCFile current = null;
for (int i=0; i<tmp.size(); i++) {
current = tmp.get(i);
return ret;
}
-
public boolean saveFile(OCFile file) {
boolean overriden = false;
ContentValues cv = new ContentValues();
cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
+ cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
boolean sameRemotePath = fileExists(file.getRemotePath());
if (sameRemotePath ||
* HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
*
* @param folder
- * @param files
- * @param removeNotUpdated
+ * @param updatedFiles
+ * @param filesToRemove
*/
public void saveFolder(
OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove
cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
+ cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
boolean existsByPath = fileExists(file.getRemotePath());
if (existsByPath || fileExists(file.getFileId())) {
if (removeLocalCopy && file.isDown() && localPath != null && success) {
success = new File(localPath).delete();
if (success) {
- triggerMediaScan(localPath);
+ deleteFileInMediaScan(localPath);
}
if (!removeDBData && success) {
// maybe unnecessary, but should be checked TODO remove if unnecessary
private boolean removeLocalFolder(OCFile folder) {
boolean success = true;
- File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder));
+ String localFolderPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder);
+ File localFolder = new File(localFolderPath);
if (localFolder.exists()) {
// stage 1: remove the local files already registered in the files database
- Vector<OCFile> files = getFolderContent(folder.getFileId());
+ Vector<OCFile> files = getFolderContent(folder.getFileId(), false);
if (files != null) {
for (OCFile file : files) {
if (file.isFolder()) {
success &= removeLocalFolder(file);
} else {
if (file.isDown()) {
- String path = file.getStoragePath();
File localFile = new File(file.getStoragePath());
success &= localFile.delete();
if (success) {
+ // notify MediaScanner about removed file
+ deleteFileInMediaScan(file.getStoragePath());
file.setStoragePath(null);
saveFile(file);
- triggerMediaScan(path); // notify MediaScanner about removed file
}
}
}
} else {
String path = localFile.getAbsolutePath();
success &= localFile.delete();
- triggerMediaScan(path); // notify MediaScanner about removed file
}
}
}
Iterator<String> it = originalPathsToTriggerMediaScan.iterator();
while (it.hasNext()) {
// Notify MediaScanner about removed file
- triggerMediaScan(it.next());
+ deleteFileInMediaScan(it.next());
}
it = newPathsToTriggerMediaScan.iterator();
while (it.hasNext()) {
}
- private Vector<OCFile> getFolderContent(long parentId) {
+ private Vector<OCFile> getFolderContent(long parentId, boolean onlyOnDevice) {
Vector<OCFile> ret = new Vector<OCFile>();
if (c.moveToFirst()) {
do {
OCFile child = createFileInstance(c);
- ret.add(child);
+ if (child.isFolder() || !onlyOnDevice || onlyOnDevice && child.isDown()){
+ ret.add(child);
+ }
} while (c.moveToNext());
}
file.setRemoteId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)));
file.setNeedsUpdateThumbnail(c.getInt(
c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
+ file.setDownloading(c.getInt(
+ c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
}
return file;
ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
file.needsUpdateThumbnail() ? 1 : 0
);
+ cv.put(
+ ProviderTableMeta.FILE_IS_DOWNLOADING,
+ file.isDownloading() ? 1 : 0
+ );
boolean existsByPath = fileExists(file.getRemotePath());
if (existsByPath || fileExists(file.getFileId())) {
}
private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(
- OCFile folder, ArrayList<ContentProviderOperation> preparedOperations
- ) {
+ OCFile folder, ArrayList<ContentProviderOperation> preparedOperations) {
if (folder != null) {
String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
+ ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
String [] whereArgs = new String[]{ "", mAccount.name };
- Vector<OCFile> files = getFolderContent(folder);
+ Vector<OCFile> files = getFolderContent(folder, false);
for (OCFile file : files) {
whereArgs[0] = file.getRemotePath();
MainApp.getAppContext().sendBroadcast(intent);
}
+ public void deleteFileInMediaScan(String path) {
+
+ String mimetypeString = FileStorageUtils.getMimeTypeFromName(path);
+ ContentResolver contentResolver = getContentResolver();
+
+ if (contentResolver != null) {
+ if (mimetypeString.startsWith("image/")) {
+ // Images
+ contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Images.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("audio/")) {
+ // Audio
+ contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Audio.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("video/")) {
+ // Video
+ contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Video.Media.DATA + "=?", new String[]{path});
+ }
+ } else {
+ ContentProviderClient contentProviderClient = getContentProviderClient();
+ try {
+ if (mimetypeString.startsWith("image/")) {
+ // Images
+ contentProviderClient.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Images.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("audio/")) {
+ // Audio
+ contentProviderClient.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Audio.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("video/")) {
+ // Video
+ contentProviderClient.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Video.Media.DATA + "=?", new String[]{path});
+ }
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception deleting media file in MediaStore " + e.getMessage());
+ }
+ }
+
+ }
+
}
--- /dev/null
- mChildren = mStorageManager.getFolderContent(mLocalFolder);
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ package com.owncloud.android.operations;
+
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileOutputStream;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.OutputStream;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Vector;
+
+ import org.apache.http.HttpStatus;
+ import android.accounts.Account;
+ import android.content.Context;
+ import android.content.Intent;
+ import android.util.Log;
+ //import android.support.v4.content.LocalBroadcastManager;
+
+ import com.owncloud.android.MainApp;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
+ import com.owncloud.android.datamodel.OCFile;
+
+ import com.owncloud.android.lib.common.OwnCloudClient;
+ import com.owncloud.android.lib.resources.shares.OCShare;
+ import com.owncloud.android.lib.common.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
+ import com.owncloud.android.lib.resources.files.FileUtils;
+ import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+ import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
+ import com.owncloud.android.lib.resources.files.RemoteFile;
+
+ import com.owncloud.android.syncadapter.FileSyncAdapter;
+ import com.owncloud.android.utils.FileStorageUtils;
+
+
+
+ /**
+ * Remote operation performing the synchronization of the list of files contained
+ * in a folder identified with its remote path.
+ *
+ * Fetches the list and properties of the files contained in the given folder, including their
+ * properties, and updates the local database with them.
+ *
+ * Does NOT enter in the child folders to synchronize their contents also.
+ */
+ public class RefreshFolderOperation extends RemoteOperation {
+
+ private static final String TAG = RefreshFolderOperation.class.getSimpleName();
+
+ public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED =
+ RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
+ public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED =
+ RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
+
+ /** Time stamp for the synchronization process in progress */
+ private long mCurrentSyncTime;
+
+ /** Remote folder to synchronize */
+ private OCFile mLocalFolder;
+
+ /** Access to the local database */
+ private FileDataStorageManager mStorageManager;
+
+ /** Account where the file to synchronize belongs */
+ private Account mAccount;
+
+ /** Android context; necessary to send requests to the download service */
+ private Context mContext;
+
+ /** Files and folders contained in the synchronized folder after a successful operation */
+ private List<OCFile> mChildren;
+
+ /** Counter of conflicts found between local and remote files */
+ private int mConflictsFound;
+
+ /** Counter of failed operations in synchronization of kept-in-sync files */
+ private int mFailsInFavouritesFound;
+
+ /**
+ * Map of remote and local paths to files that where locally stored in a location
+ * out of the ownCloud folder and couldn't be copied automatically into it
+ **/
+ private Map<String, String> mForgottenLocalFiles;
+
+ /** 'True' means that this operation is part of a full account synchronization */
+ private boolean mSyncFullAccount;
+
+ /** 'True' means that Share resources bound to the files into should be refreshed also */
+ private boolean mIsShareSupported;
+
+ /** 'True' means that the remote folder changed and should be fetched */
+ private boolean mRemoteFolderChanged;
+
+ /** 'True' means that Etag will be ignored */
+ private boolean mIgnoreETag;
+
+
+ /**
+ * Creates a new instance of {@link RefreshFolderOperation}.
+ *
+ * @param folder Folder to synchronize.
+ * @param currentSyncTime Time stamp for the synchronization process in progress.
+ * @param syncFullAccount 'True' means that this operation is part of a full account
+ * synchronization.
+ * @param isShareSupported 'True' means that the server supports the sharing API.
+ * @param ignoreETag 'True' means that the content of the remote folder should
+ * be fetched and updated even though the 'eTag' did not
+ * change.
+ * @param dataStorageManager Interface with the local database.
+ * @param account ownCloud account where the folder is located.
+ * @param context Application context.
+ */
+ public RefreshFolderOperation(OCFile folder,
+ long currentSyncTime,
+ boolean syncFullAccount,
+ boolean isShareSupported,
+ boolean ignoreETag,
+ FileDataStorageManager dataStorageManager,
+ Account account,
+ Context context) {
+ mLocalFolder = folder;
+ mCurrentSyncTime = currentSyncTime;
+ mSyncFullAccount = syncFullAccount;
+ mIsShareSupported = isShareSupported;
+ mStorageManager = dataStorageManager;
+ mAccount = account;
+ mContext = context;
+ mForgottenLocalFiles = new HashMap<String, String>();
+ mRemoteFolderChanged = false;
+ mIgnoreETag = ignoreETag;
+ }
+
+
+ public int getConflictsFound() {
+ return mConflictsFound;
+ }
+
+ public int getFailsInFavouritesFound() {
+ return mFailsInFavouritesFound;
+ }
+
+ public Map<String, String> getForgottenLocalFiles() {
+ return mForgottenLocalFiles;
+ }
+
+ /**
+ * Returns the list of files and folders contained in the synchronized folder,
+ * if called after synchronization is complete.
+ *
+ * @return List of files and folders contained in the synchronized folder.
+ */
+ public List<OCFile> getChildren() {
+ return mChildren;
+ }
+
+ /**
+ * Performs the synchronization.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+ mFailsInFavouritesFound = 0;
+ mConflictsFound = 0;
+ mForgottenLocalFiles.clear();
+
+ if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) {
+ updateOCVersion(client);
+ }
+
+ result = checkForChanges(client);
+
+ if (result.isSuccess()) {
+ if (mRemoteFolderChanged) {
+ result = fetchAndSyncRemoteFolder(client);
+ } else {
- List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder);
++ mChildren = mStorageManager.getFolderContent(mLocalFolder, false);
+ }
+ }
+
+ if (!mSyncFullAccount) {
+ sendLocalBroadcast(
+ EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
+ );
+ }
+
+ if (result.isSuccess() && mIsShareSupported && !mSyncFullAccount) {
+ refreshSharesForFolder(client); // share result is ignored
+ }
+
+ if (!mSyncFullAccount) {
+ sendLocalBroadcast(
+ EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
+ );
+ }
+
+ return result;
+
+ }
+
+
+ private void updateOCVersion(OwnCloudClient client) {
+ UpdateOCVersionOperation update = new UpdateOCVersionOperation(mAccount, mContext);
+ RemoteOperationResult result = update.execute(client);
+ if (result.isSuccess()) {
+ mIsShareSupported = update.getOCVersion().isSharedSupported();
+ }
+ }
+
+
+ private RemoteOperationResult checkForChanges(OwnCloudClient client) {
+ mRemoteFolderChanged = true;
+ RemoteOperationResult result = null;
+ String remotePath = null;
+
+ remotePath = mLocalFolder.getRemotePath();
+ Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
+
+ // remote request
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+ result = operation.execute(client);
+ if (result.isSuccess()){
+ OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+
+ if (!mIgnoreETag) {
+ // check if remote and local folder are different
+ mRemoteFolderChanged =
+ !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
+ }
+
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ (mRemoteFolderChanged ? "changed" : "not changed"));
+
+ } else {
+ // check failed
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+ removeLocalFolder();
+ }
+ if (result.isException()) {
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ result.getLogMessage(), result.getException());
+ } else {
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ result.getLogMessage());
+ }
+ }
+
+ return result;
+ }
+
+
+ private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) {
+ String remotePath = mLocalFolder.getRemotePath();
+ ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(remotePath);
+ RemoteOperationResult result = operation.execute(client);
+ Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
+
+ if (result.isSuccess()) {
+ synchronizeData(result.getData(), client);
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+ // should be a different result code, but will do the job
+ }
+ } else {
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND)
+ removeLocalFolder();
+ }
+
+ return result;
+ }
+
+
+ private void removeLocalFolder() {
+ if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
+ String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ mStorageManager.removeFolder(
+ mLocalFolder,
+ true,
+ ( mLocalFolder.isDown() &&
+ mLocalFolder.getStoragePath().startsWith(currentSavePath)
+ )
+ );
+ }
+ }
+
+
+ /**
+ * Synchronizes the data retrieved from the server about the contents of the target folder
+ * with the current data in the local database.
+ *
+ * Grants that mChildren is updated with fresh data after execution.
+ *
+ * @param folderAndFiles Remote folder and children files in Folder
+ *
+ * @param client Client instance to the remote server where the data were
+ * retrieved.
+ * @return 'True' when any change was made in the local data, 'false' otherwise
+ */
+ private void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client) {
+ // get 'fresh data' from the database
+ mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
+
+ // parse data from remote folder
+ OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
+ remoteFolder.setParentId(mLocalFolder.getParentId());
+ remoteFolder.setFileId(mLocalFolder.getFileId());
+
+ Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+ + " changed - starting update of local data ");
+
+ List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
+ List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+
+ // get current data about local contents of the folder to synchronize
++ List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder, true);
+ Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
+ for (OCFile file : localFiles) {
+ localFilesMap.put(file.getRemotePath(), file);
+ }
+
+ // loop to update every child
+ OCFile remoteFile = null, localFile = null;
+ for (int i=1; i<folderAndFiles.size(); i++) {
+ /// new OCFile instance with the data from the server
+ remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
+ remoteFile.setParentId(mLocalFolder.getFileId());
+
+ /// retrieve local data for the read file
+ // localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
+ localFile = localFilesMap.remove(remoteFile.getRemotePath());
+
+ /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in server)
+ remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
+ if (localFile != null) {
+ // some properties of local state are kept unmodified
+ remoteFile.setFileId(localFile.getFileId());
+ remoteFile.setKeepInSync(localFile.keepInSync());
+ remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
+ remoteFile.setModificationTimestampAtLastSyncForData(
+ localFile.getModificationTimestampAtLastSyncForData()
+ );
+ remoteFile.setStoragePath(localFile.getStoragePath());
+ // eTag will not be updated unless contents are synchronized
+ // (Synchronize[File|Folder]Operation with remoteFile as parameter)
+ remoteFile.setEtag(localFile.getEtag());
+ if (remoteFile.isFolder()) {
+ remoteFile.setFileLength(localFile.getFileLength());
+ // TODO move operations about size of folders to FileContentProvider
+ } else if (mRemoteFolderChanged && remoteFile.isImage() &&
+ remoteFile.getModificationTimestamp() !=
+ localFile.getModificationTimestamp()) {
+ remoteFile.setNeedsUpdateThumbnail(true);
+ Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
+ }
+ remoteFile.setPublicLink(localFile.getPublicLink());
+ remoteFile.setShareByLink(localFile.isShareByLink());
+ } else {
+ // remote eTag will not be updated unless contents are synchronized
+ // (Synchronize[File|Folder]Operation with remoteFile as parameter)
+ remoteFile.setEtag("");
+ }
+
+ /// check and fix, if needed, local storage path
+ checkAndFixForeignStoragePath(remoteFile); // policy - local files are COPIED
+ // into the ownCloud local folder;
+ searchForLocalFileInDefaultPath(remoteFile); // legacy
+
+ /// prepare content synchronization for kept-in-sync files
+ if (remoteFile.keepInSync()) {
+ SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
+ remoteFile,
+ mAccount,
+ true,
+ mContext
+ );
+
+ filesToSyncContents.add(operation);
+ }
+
+ updatedFiles.add(remoteFile);
+ }
+
+ // save updated contents in local database
+ mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+ // request for the synchronization of file contents AFTER saving current remote properties
+ startContentSynchronizations(filesToSyncContents, client);
+
+ mChildren = updatedFiles;
+ }
+
+ /**
+ * Performs a list of synchronization operations, determining if a download or upload is needed
+ * or if exists conflict due to changes both in local and remote contents of the each file.
+ *
+ * If download or upload is needed, request the operation to the corresponding service and goes
+ * on.
+ *
+ * @param filesToSyncContents Synchronization operations to execute.
+ * @param client Interface to the remote ownCloud server.
+ */
+ private void startContentSynchronizations(
+ List<SynchronizeFileOperation> filesToSyncContents, OwnCloudClient client
+ ) {
+ RemoteOperationResult contentsResult = null;
+ for (SynchronizeFileOperation op: filesToSyncContents) {
+ contentsResult = op.execute(mStorageManager, mContext); // async
+ if (!contentsResult.isSuccess()) {
+ if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound++;
+ } else {
+ mFailsInFavouritesFound++;
+ if (contentsResult.getException() != null) {
+ Log_OC.e(TAG, "Error while synchronizing favourites : "
+ + contentsResult.getLogMessage(), contentsResult.getException());
+ } else {
+ Log_OC.e(TAG, "Error while synchronizing favourites : "
+ + contentsResult.getLogMessage());
+ }
+ }
+ } // won't let these fails break the synchronization process
+ }
+ }
+
+
+ public boolean isMultiStatus(int status) {
+ return (status == HttpStatus.SC_MULTI_STATUS);
+ }
+
+ /**
+ * Creates and populates a new {@link OCFile} object with the data read from the server.
+ *
+ * @param remote remote file read from the server (remote file or folder).
+ * @return New OCFile instance representing the remote resource described by we.
+ */
+ private OCFile fillOCFile(RemoteFile remote) {
+ OCFile file = new OCFile(remote.getRemotePath());
+ file.setCreationTimestamp(remote.getCreationTimestamp());
+ file.setFileLength(remote.getLength());
+ file.setMimetype(remote.getMimeType());
+ file.setModificationTimestamp(remote.getModifiedTimestamp());
+ file.setEtag(remote.getEtag());
+ file.setPermissions(remote.getPermissions());
+ file.setRemoteId(remote.getRemoteId());
+ return file;
+ }
+
+
+ /**
+ * Checks the storage path of the OCFile received as parameter.
+ * If it's out of the local ownCloud folder, tries to copy the file inside it.
+ *
+ * If the copy fails, the link to the local file is nullified. The account of forgotten
+ * files is kept in {@link #mForgottenLocalFiles}
+ *)
+ * @param file File to check and fix.
+ */
+ private void checkAndFixForeignStoragePath(OCFile file) {
+ String storagePath = file.getStoragePath();
+ String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
+ if (storagePath != null && !storagePath.equals(expectedPath)) {
+ /// fix storagePaths out of the local ownCloud folder
+ File originalFile = new File(storagePath);
+ if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } else {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ File expectedFile = new File(expectedPath);
+ File expectedParent = expectedFile.getParentFile();
+ expectedParent.mkdirs();
+ if (!expectedParent.isDirectory()) {
+ throw new IOException(
+ "Unexpected error: parent directory could not be created"
+ );
+ }
+ expectedFile.createNewFile();
+ if (!expectedFile.isFile()) {
+ throw new IOException("Unexpected error: target file could not be created");
+ }
+ in = new FileInputStream(originalFile);
+ out = new FileOutputStream(expectedFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0){
+ out.write(buf, 0, len);
+ }
+ file.setStoragePath(expectedPath);
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } finally {
+ try {
+ if (in != null) in.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing input stream for "
+ + storagePath + " (ignoring)", e);
+ }
+ try {
+ if (out != null) out.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing output stream for "
+ + expectedPath + " (ignoring)", e);
+ }
+ }
+ }
+ }
+ }
+
+
+ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+
+ // remote request
+ GetRemoteSharesForFileOperation operation =
+ new GetRemoteSharesForFileOperation(mLocalFolder.getRemotePath(), false, true);
+ result = operation.execute(client);
+
+ if (result.isSuccess()) {
+ // update local database
+ ArrayList<OCShare> shares = new ArrayList<OCShare>();
+ for(Object obj: result.getData()) {
+ shares.add((OCShare) obj);
+ }
+ mStorageManager.saveSharesInFolder(shares, mLocalFolder);
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Scans the default location for saving local copies of files searching for
+ * a 'lost' file with the same full name as the {@link OCFile} received as
+ * parameter.
+ *
+ * @param file File to associate a possible 'lost' local file.
+ */
+ private void searchForLocalFileInDefaultPath(OCFile file) {
+ if (file.getStoragePath() == null && !file.isFolder()) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
+ file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
+ }
+ }
+
+
+ /**
+ * Sends a message to any application component interested in the progress
+ * of the synchronization.
+ *
+ * @param event
+ * @param dirRemotePath Remote path of a folder that was just synchronized
+ * (with or without success)
+ * @param result
+ */
+ private void sendLocalBroadcast(
+ String event, String dirRemotePath, RemoteOperationResult result
+ ) {
+ Log_OC.d(TAG, "Send broadcast " + event);
+ Intent intent = new Intent(event);
+ intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name);
+ if (dirRemotePath != null) {
+ intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath);
+ }
+ intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result);
+ mContext.sendStickyBroadcast(intent);
+ //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
+ }
+
+
+ public boolean getRemoteFolderChanged() {
+ return mRemoteFolderChanged;
+ }
+
+ }
- /* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
package com.owncloud.android.operations;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.http.HttpStatus;
+
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
- //import android.support.v4.content.LocalBroadcastManager;
+ import com.owncloud.android.MainApp;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.lib.common.OwnCloudClient;
+ import com.owncloud.android.lib.common.operations.OperationCancelledException;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.syncadapter.FileSyncAdapter;
+ import com.owncloud.android.operations.common.SyncOperation;
+ import com.owncloud.android.services.OperationsService;
import com.owncloud.android.utils.FileStorageUtils;
+ import java.io.File;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Vector;
+ import java.util.concurrent.atomic.AtomicBoolean;
+
+ //import android.support.v4.content.LocalBroadcastManager;
/**
* properties, and updates the local database with them.
*
* Does NOT enter in the child folders to synchronize their contents also.
- *
- * @author David A. Velasco
*/
- public class SynchronizeFolderOperation extends RemoteOperation {
+ public class SynchronizeFolderOperation extends SyncOperation {
private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
- public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED =
- SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
- public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED =
- SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
-
/** Time stamp for the synchronization process in progress */
private long mCurrentSyncTime;
-
- /** Remote folder to synchronize */
- private OCFile mLocalFolder;
-
- /** Access to the local database */
- private FileDataStorageManager mStorageManager;
+
+ /** Remote path of the folder to synchronize */
+ private String mRemotePath;
/** Account where the file to synchronize belongs */
private Account mAccount;
-
+
/** Android context; necessary to send requests to the download service */
private Context mContext;
-
+
+ /** Locally cached information about folder to synchronize */
+ private OCFile mLocalFolder;
+
/** Files and folders contained in the synchronized folder after a successful operation */
- private List<OCFile> mChildren;
+ //private List<OCFile> mChildren;
/** Counter of conflicts found between local and remote files */
private int mConflictsFound;
/** Counter of failed operations in synchronization of kept-in-sync files */
- private int mFailsInFavouritesFound;
+ private int mFailsInFileSyncsFound;
- /**
- * Map of remote and local paths to files that where locally stored in a location
- * out of the ownCloud folder and couldn't be copied automatically into it
- **/
- private Map<String, String> mForgottenLocalFiles;
-
- /** 'True' means that this operation is part of a full account synchronization */
- private boolean mSyncFullAccount;
-
- /** 'True' means that Share resources bound to the files into should be refreshed also */
- private boolean mIsShareSupported;
-
/** 'True' means that the remote folder changed and should be fetched */
private boolean mRemoteFolderChanged;
- /** 'True' means that Etag will be ignored */
- private boolean mIgnoreETag;
-
+ private List<OCFile> mFilesForDirectDownload;
+ // to avoid extra PROPFINDs when there was no change in the folder
+ private List<SyncOperation> mFilesToSyncContentsWithoutUpload;
+ // this will go out when 'folder synchronization' replaces 'folder download'; step by step
+
+ private List<SyncOperation> mFavouriteFilesToSyncContents;
+ // this will be used for every file when 'folder synchronization' replaces 'folder download'
+
+ private final AtomicBoolean mCancellationRequested;
+
/**
* Creates a new instance of {@link SynchronizeFolderOperation}.
- *
- * @param folder Folder to synchronize.
- * @param currentSyncTime Time stamp for the synchronization process in progress.
- * @param syncFullAccount 'True' means that this operation is part of a full account
- * synchronization.
- * @param isShareSupported 'True' means that the server supports the sharing API.
- * @param ignoreEtag 'True' means that the content of the remote folder should
- * be fetched and updated even though the 'eTag' did not
- * change.
- * @param dataStorageManager Interface with the local database.
- * @param account ownCloud account where the folder is located.
+ *
* @param context Application context.
+ * @param remotePath Path to synchronize.
+ * @param account ownCloud account where the folder is located.
+ * @param currentSyncTime Time stamp for the synchronization process in progress.
*/
- public SynchronizeFolderOperation( OCFile folder,
- long currentSyncTime,
- boolean syncFullAccount,
- boolean isShareSupported,
- boolean ignoreETag,
- FileDataStorageManager dataStorageManager,
- Account account,
- Context context ) {
- mLocalFolder = folder;
+ public SynchronizeFolderOperation(Context context, String remotePath, Account account,
+ long currentSyncTime){
+ mRemotePath = remotePath;
mCurrentSyncTime = currentSyncTime;
- mSyncFullAccount = syncFullAccount;
- mIsShareSupported = isShareSupported;
- mStorageManager = dataStorageManager;
mAccount = account;
mContext = context;
- mForgottenLocalFiles = new HashMap<String, String>();
mRemoteFolderChanged = false;
- mIgnoreETag = ignoreETag;
+ mFilesForDirectDownload = new Vector<OCFile>();
+ mFilesToSyncContentsWithoutUpload = new Vector<SyncOperation>();
+ mFavouriteFilesToSyncContents = new Vector<SyncOperation>();
+ mCancellationRequested = new AtomicBoolean(false);
}
-
-
+
+
public int getConflictsFound() {
return mConflictsFound;
}
-
- public int getFailsInFavouritesFound() {
- return mFailsInFavouritesFound;
- }
-
- public Map<String, String> getForgottenLocalFiles() {
- return mForgottenLocalFiles;
- }
-
- /**
- * Returns the list of files and folders contained in the synchronized folder,
- * if called after synchronization is complete.
- *
- * @return List of files and folders contained in the synchronized folder.
- */
- public List<OCFile> getChildren() {
- return mChildren;
+
+ public int getFailsInFileSyncsFound() {
+ return mFailsInFileSyncsFound;
}
-
+
/**
* Performs the synchronization.
- *
+ *
* {@inheritDoc}
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
- mFailsInFavouritesFound = 0;
+ mFailsInFileSyncsFound = 0;
mConflictsFound = 0;
- mForgottenLocalFiles.clear();
-
- if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) {
- updateOCVersion(client);
- }
-
- result = checkForChanges(client);
- if (result.isSuccess()) {
- if (mRemoteFolderChanged) {
- result = fetchAndSyncRemoteFolder(client);
- } else {
- mChildren = mStorageManager.getFolderContent(mLocalFolder, false);
+ try {
+ // get locally cached information about folder
+ mLocalFolder = getStorageManager().getFileByPath(mRemotePath);
+
+ result = checkForChanges(client);
+
+ if (result.isSuccess()) {
+ if (mRemoteFolderChanged) {
+ result = fetchAndSyncRemoteFolder(client);
+
+ } else {
+ prepareOpsFromLocalKnowledge();
+ }
+
+ if (result.isSuccess()) {
+ syncContents(client);
+ }
+
}
+
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+
+ } catch (OperationCancelledException e) {
+ result = new RemoteOperationResult(e);
}
-
- if (!mSyncFullAccount) {
- sendLocalBroadcast(
- EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
- );
- }
-
- if (result.isSuccess() && mIsShareSupported && !mSyncFullAccount) {
- refreshSharesForFolder(client); // share result is ignored
- }
-
- if (!mSyncFullAccount) {
- sendLocalBroadcast(
- EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
- );
- }
-
- return result;
-
- }
+ return result;
- private void updateOCVersion(OwnCloudClient client) {
- UpdateOCVersionOperation update = new UpdateOCVersionOperation(mAccount, mContext);
- RemoteOperationResult result = update.execute(client);
- if (result.isSuccess()) {
- mIsShareSupported = update.getOCVersion().isSharedSupported();
- }
}
-
- private RemoteOperationResult checkForChanges(OwnCloudClient client) {
+ private RemoteOperationResult checkForChanges(OwnCloudClient client)
+ throws OperationCancelledException {
+ Log_OC.d(TAG, "Checking changes in " + mAccount.name + mRemotePath);
+
mRemoteFolderChanged = true;
RemoteOperationResult result = null;
- String remotePath = null;
-
- remotePath = mLocalFolder.getRemotePath();
- Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
- // remote request
- ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+
+ // remote request
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mRemotePath);
result = operation.execute(client);
if (result.isSuccess()){
OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
- if (!mIgnoreETag) {
- // check if remote and local folder are different
- mRemoteFolderChanged =
+ // check if remote and local folder are different
+ mRemoteFolderChanged =
!(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
- }
result = new RemoteOperationResult(ResultCode.OK);
-
- Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " +
+
+ Log_OC.i(TAG, "Checked " + mAccount.name + mRemotePath + " : " +
(mRemoteFolderChanged ? "changed" : "not changed"));
-
+
} else {
// check failed
if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
removeLocalFolder();
}
if (result.isException()) {
- Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ Log_OC.e(TAG, "Checked " + mAccount.name + mRemotePath + " : " +
result.getLogMessage(), result.getException());
} else {
- Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ Log_OC.e(TAG, "Checked " + mAccount.name + mRemotePath + " : " +
result.getLogMessage());
}
+
}
-
+
return result;
}
- private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) {
- String remotePath = mLocalFolder.getRemotePath();
- ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(remotePath);
- RemoteOperationResult result = operation.execute(client);
- Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
+ private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client)
+ throws OperationCancelledException {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(mRemotePath);
+ RemoteOperationResult result = operation.execute(client);
+ Log_OC.d(TAG, "Synchronizing " + mAccount.name + mRemotePath);
+
if (result.isSuccess()) {
synchronizeData(result.getData(), client);
- if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+ if (mConflictsFound > 0 || mFailsInFileSyncsFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
// should be a different result code, but will do the job
}
} else {
removeLocalFolder();
}
+
return result;
}
-
+
private void removeLocalFolder() {
- if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
+ FileDataStorageManager storageManager = getStorageManager();
+ if (storageManager.fileExists(mLocalFolder.getFileId())) {
String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
- mStorageManager.removeFolder(
- mLocalFolder,
- true,
- ( mLocalFolder.isDown() &&
+ storageManager.removeFolder(
+ mLocalFolder,
+ true,
+ ( mLocalFolder.isDown() && // TODO: debug, I think this is
+ // always false for folders
mLocalFolder.getStoragePath().startsWith(currentSavePath)
)
);
/**
- * Synchronizes the data retrieved from the server about the contents of the target folder
+ * Synchronizes the data retrieved from the server about the contents of the target folder
* with the current data in the local database.
- *
+ *
* Grants that mChildren is updated with fresh data after execution.
- *
- * @param folderAndFiles Remote folder and children files in Folder
- *
- * @param client Client instance to the remote server where the data were
- * retrieved.
+ *
+ * @param folderAndFiles Remote folder and children files in Folder
+ *
+ * @param client Client instance to the remote server where the data were
+ * retrieved.
* @return 'True' when any change was made in the local data, 'false' otherwise
*/
- private void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client) {
- // get 'fresh data' from the database
- mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
-
- // parse data from remote folder
+ private void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client)
+ throws OperationCancelledException {
+ FileDataStorageManager storageManager = getStorageManager();
+
+ // parse data from remote folder
OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
remoteFolder.setParentId(mLocalFolder.getParentId());
remoteFolder.setFileId(mLocalFolder.getFileId());
-
- Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+
+ Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+ " changed - starting update of local data ");
-
+
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
- List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+ mFilesForDirectDownload.clear();
+ mFilesToSyncContentsWithoutUpload.clear();
+ mFavouriteFilesToSyncContents.clear();
+
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
// get current data about local contents of the folder to synchronize
- List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder, false);
- List<OCFile> localFiles = storageManager.getFolderContent(mLocalFolder);
++ List<OCFile> localFiles = storageManager.getFolderContent(mLocalFolder, true);
Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
for (OCFile file : localFiles) {
localFilesMap.put(file.getRemotePath(), file);
}
-
- // loop to update every child
+
+ // loop to synchronize every child
OCFile remoteFile = null, localFile = null;
for (int i=1; i<folderAndFiles.size(); i++) {
/// new OCFile instance with the data from the server
remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
remoteFile.setParentId(mLocalFolder.getFileId());
- /// retrieve local data for the read file
+ /// retrieve local data for the read file
// localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
localFile = localFilesMap.remove(remoteFile.getRemotePath());
-
+
/// add to the remoteFile (the new one) data about LOCAL STATE (not existing in server)
remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
if (localFile != null) {
localFile.getModificationTimestampAtLastSyncForData()
);
remoteFile.setStoragePath(localFile.getStoragePath());
- // eTag will not be updated unless contents are synchronized
+ // eTag will not be updated unless contents are synchronized
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
- remoteFile.setEtag(localFile.getEtag());
+ remoteFile.setEtag(localFile.getEtag());
if (remoteFile.isFolder()) {
- remoteFile.setFileLength(localFile.getFileLength());
+ remoteFile.setFileLength(localFile.getFileLength());
// TODO move operations about size of folders to FileContentProvider
} else if (mRemoteFolderChanged && remoteFile.isImage() &&
- remoteFile.getModificationTimestamp() != localFile.getModificationTimestamp()) {
+ remoteFile.getModificationTimestamp() !=
+ localFile.getModificationTimestamp()) {
remoteFile.setNeedsUpdateThumbnail(true);
Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
}
remoteFile.setPublicLink(localFile.getPublicLink());
remoteFile.setShareByLink(localFile.isShareByLink());
} else {
- // remote eTag will not be updated unless contents are synchronized
+ // remote eTag will not be updated unless contents are synchronized
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
- remoteFile.setEtag("");
+ remoteFile.setEtag("");
}
/// check and fix, if needed, local storage path
- checkAndFixForeignStoragePath(remoteFile); // policy - local files are COPIED
- // into the ownCloud local folder;
- searchForLocalFileInDefaultPath(remoteFile); // legacy
-
- /// prepare content synchronization for kept-in-sync files
- if (remoteFile.keepInSync()) {
- SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
- remoteFile,
- mAccount,
- true,
- mContext
- );
+ searchForLocalFileInDefaultPath(remoteFile);
+
+ /// classify file to sync/download contents later
+ if (remoteFile.isFolder()) {
+ /// to download children files recursively
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ startSyncFolderOperation(remoteFile.getRemotePath());
+ }
+
+ } else if (remoteFile.keepInSync()) {
+ /// prepare content synchronization for kept-in-sync files
+ SynchronizeFileOperation operation = new SynchronizeFileOperation(
+ localFile,
+ remoteFile,
+ mAccount,
+ true,
+ mContext
+ );
+ mFavouriteFilesToSyncContents.add(operation);
- filesToSyncContents.add(operation);
+ } else {
+ /// prepare limited synchronization for regular files
+ SynchronizeFileOperation operation = new SynchronizeFileOperation(
+ localFile,
+ remoteFile,
+ mAccount,
+ true,
+ false,
+ mContext
+ );
+ mFilesToSyncContentsWithoutUpload.add(operation);
}
-
+
updatedFiles.add(remoteFile);
}
// save updated contents in local database
- mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+ storageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+ }
+
+
+ private void prepareOpsFromLocalKnowledge() throws OperationCancelledException {
- List<OCFile> children = getStorageManager().getFolderContent(mLocalFolder);
++ // TODO TOBI ist das richtig?
++ List<OCFile> children = getStorageManager().getFolderContent(mLocalFolder, true);
+ for (OCFile child : children) {
+ /// classify file to sync/download contents later
+ if (child.isFolder()) {
+ /// to download children files recursively
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ startSyncFolderOperation(child.getRemotePath());
+ }
- // request for the synchronization of file contents AFTER saving current remote properties
- startContentSynchronizations(filesToSyncContents, client);
+ } else {
+ /// prepare limited synchronization for regular files
+ if (!child.isDown()) {
+ mFilesForDirectDownload.add(child);
+ }
+ }
+ }
+ }
- mChildren = updatedFiles;
+
+ private void syncContents(OwnCloudClient client) throws OperationCancelledException {
+ startDirectDownloads();
+ startContentSynchronizations(mFilesToSyncContentsWithoutUpload, client);
+ startContentSynchronizations(mFavouriteFilesToSyncContents, client);
+ }
+
+
+ private void startDirectDownloads() throws OperationCancelledException {
+ for (OCFile file : mFilesForDirectDownload) {
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ Intent i = new Intent(mContext, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
+ mContext.startService(i);
+ }
+ }
}
/**
* Performs a list of synchronization operations, determining if a download or upload is needed
* or if exists conflict due to changes both in local and remote contents of the each file.
- *
- * If download or upload is needed, request the operation to the corresponding service and goes
+ *
+ * If download or upload is needed, request the operation to the corresponding service and goes
* on.
- *
+ *
* @param filesToSyncContents Synchronization operations to execute.
* @param client Interface to the remote ownCloud server.
*/
- private void startContentSynchronizations(
- List<SynchronizeFileOperation> filesToSyncContents, OwnCloudClient client
- ) {
+ private void startContentSynchronizations(List<SyncOperation> filesToSyncContents,
+ OwnCloudClient client)
+ throws OperationCancelledException {
+
+ Log_OC.v(TAG, "Starting content synchronization... ");
RemoteOperationResult contentsResult = null;
- for (SynchronizeFileOperation op: filesToSyncContents) {
- contentsResult = op.execute(mStorageManager, mContext); // async
+ for (SyncOperation op: filesToSyncContents) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ contentsResult = op.execute(getStorageManager(), mContext);
if (!contentsResult.isSuccess()) {
if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
mConflictsFound++;
} else {
- mFailsInFavouritesFound++;
+ mFailsInFileSyncsFound++;
if (contentsResult.getException() != null) {
- Log_OC.e(TAG, "Error while synchronizing favourites : "
+ Log_OC.e(TAG, "Error while synchronizing file : "
+ contentsResult.getLogMessage(), contentsResult.getException());
} else {
- Log_OC.e(TAG, "Error while synchronizing favourites : "
+ Log_OC.e(TAG, "Error while synchronizing file : "
+ contentsResult.getLogMessage());
}
}
+ // TODO - use the errors count in notifications
} // won't let these fails break the synchronization process
}
}
-
- public boolean isMultiStatus(int status) {
- return (status == HttpStatus.SC_MULTI_STATUS);
- }
-
+
/**
- * Creates and populates a new {@link OCFile} object with the data read from the server.
- *
+ * Creates and populates a new {@link com.owncloud.android.datamodel.OCFile}
+ * object with the data read from the server.
+ *
* @param remote remote file read from the server (remote file or folder).
* @return New OCFile instance representing the remote resource described by we.
*/
file.setRemoteId(remote.getRemoteId());
return file;
}
-
- /**
- * Checks the storage path of the OCFile received as parameter.
- * If it's out of the local ownCloud folder, tries to copy the file inside it.
- *
- * If the copy fails, the link to the local file is nullified. The account of forgotten
- * files is kept in {@link #mForgottenLocalFiles}
- *)
- * @param file File to check and fix.
- */
- private void checkAndFixForeignStoragePath(OCFile file) {
- String storagePath = file.getStoragePath();
- String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
- if (storagePath != null && !storagePath.equals(expectedPath)) {
- /// fix storagePaths out of the local ownCloud folder
- File originalFile = new File(storagePath);
- if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
- mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
- file.setStoragePath(null);
-
- } else {
- InputStream in = null;
- OutputStream out = null;
- try {
- File expectedFile = new File(expectedPath);
- File expectedParent = expectedFile.getParentFile();
- expectedParent.mkdirs();
- if (!expectedParent.isDirectory()) {
- throw new IOException(
- "Unexpected error: parent directory could not be created"
- );
- }
- expectedFile.createNewFile();
- if (!expectedFile.isFile()) {
- throw new IOException("Unexpected error: target file could not be created");
- }
- in = new FileInputStream(originalFile);
- out = new FileOutputStream(expectedFile);
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0){
- out.write(buf, 0, len);
- }
- file.setStoragePath(expectedPath);
-
- } catch (Exception e) {
- Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
- mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
- file.setStoragePath(null);
-
- } finally {
- try {
- if (in != null) in.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing input stream for "
- + storagePath + " (ignoring)", e);
- }
- try {
- if (out != null) out.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing output stream for "
- + expectedPath + " (ignoring)", e);
- }
- }
- }
- }
- }
-
-
- private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
- RemoteOperationResult result = null;
-
- // remote request
- GetRemoteSharesForFileOperation operation =
- new GetRemoteSharesForFileOperation(mLocalFolder.getRemotePath(), false, true);
- result = operation.execute(client);
-
- if (result.isSuccess()) {
- // update local database
- ArrayList<OCShare> shares = new ArrayList<OCShare>();
- for(Object obj: result.getData()) {
- shares.add((OCShare) obj);
- }
- mStorageManager.saveSharesInFolder(shares, mLocalFolder);
- }
-
- return result;
- }
-
/**
* Scans the default location for saving local copies of files searching for
- * a 'lost' file with the same full name as the {@link OCFile} received as
- * parameter.
+ * a 'lost' file with the same full name as the {@link com.owncloud.android.datamodel.OCFile}
+ * received as parameter.
*
* @param file File to associate a possible 'lost' local file.
*/
/**
- * Sends a message to any application component interested in the progress
- * of the synchronization.
- *
- * @param event
- * @param dirRemotePath Remote path of a folder that was just synchronized
- * (with or without success)
- * @param result
+ * Cancel operation
*/
- private void sendLocalBroadcast(
- String event, String dirRemotePath, RemoteOperationResult result
- ) {
- Log_OC.d(TAG, "Send broadcast " + event);
- Intent intent = new Intent(event);
- intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name);
- if (dirRemotePath != null) {
- intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath);
- }
- intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result);
- mContext.sendStickyBroadcast(intent);
- //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
+ public void cancel() {
+ mCancellationRequested.set(true);
}
+ public String getFolderPath() {
+ String path = mLocalFolder.getStoragePath();
+ if (path != null && path.length() > 0) {
+ return path;
+ }
+ return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mLocalFolder);
+ }
- public boolean getRemoteFolderChanged() {
- return mRemoteFolderChanged;
+ private void startSyncFolderOperation(String path){
+ Intent intent = new Intent(mContext, OperationsService.class);
+ intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
+ intent.putExtra(OperationsService.EXTRA_ACCOUNT, mAccount);
+ intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, path);
+ mContext.startService(intent);
}
+ public String getRemotePath() {
+ return mRemotePath;
+ }
}
- /* ownCloud Android client application
+ /**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
package com.owncloud.android.ui.activity;
import java.io.File;
- import java.io.IOException;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
import android.annotation.TargetApi;
import android.app.AlertDialog;
- import android.app.Dialog;
- import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
- import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SyncRequest;
+import android.content.res.Configuration;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
- import android.os.Environment;
import android.os.IBinder;
import android.preference.PreferenceManager;
- import android.provider.DocumentsContract;
- import android.provider.MediaStore;
import android.provider.OpenableColumns;
+import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
-import com.owncloud.android.BuildConfig;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation;
- import com.owncloud.android.operations.SynchronizeFolderOperation;
+ import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.UnshareLinkOperation;
import com.owncloud.android.services.observer.FileObserverService;
import com.owncloud.android.syncadapter.FileSyncAdapter;
+import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter;
+ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
+ import com.owncloud.android.ui.dialog.UploadSourceDialogFragment;
import com.owncloud.android.ui.fragment.FileDetailFragment;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.ui.fragment.OCFileListFragment;
import com.owncloud.android.ui.preview.PreviewVideoActivity;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter;
+ import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.UriUtils;
/**
* Displays, what files the user has available in his ownCloud.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
*/
public class FileDisplayActivity extends HookActivity implements
private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND";
- public static final int DIALOG_SHORT_WAIT = 0;
- private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
- private static final int DIALOG_CERT_NOT_SAVED = 2;
-
public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
- private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
- private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
+ public static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
+ public static final int ACTION_SELECT_MULTIPLE_FILES = 2;
public static final int ACTION_MOVE_FILES = 3;
private static final String TAG = FileDisplayActivity.class.getSimpleName();
private boolean mSyncInProgress = false;
- private String DIALOG_UNTRUSTED_CERT;
-
+ private static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
+ private 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 DrawerLayout mDrawerLayout;
+ private ActionBarDrawerToggle mDrawerToggle;
+ private boolean showAccounts = false;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate() start");
+ Log_OC.v(TAG, "onCreate() start");
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
+
super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
- // PIN CODE request ; best location is to decide, let's try this first
- if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
- requestPinCode();
- } else if (getIntent().getAction() == null && savedInstanceState == null) {
- requestPinCode();
- }
-
- /// grant that FileObserverService is watching favourite files
+ /// grant that FileObserverService is watching favorite files
if (savedInstanceState == null) {
Intent initObserversIntent = FileObserverService.makeInitIntent(this);
startService(initObserversIntent);
/// USER INTERFACE
// Inflate and set the layout view
- setContentView(R.layout.files);
+ setContentView(R.layout.files);
+
+ // TODO move to another place that all activity can use it
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+ mDrawerToggle = new ActionBarDrawerToggle(
+ this,
+ mDrawerLayout,
+ R.drawable.ic_drawer,
+ R.string.drawer_open,
+ R.string.empty
+ ) {
+
+ /** Called when a drawer has settled in a completely closed state. */
+ public void onDrawerClosed(View view) {
+ super.onDrawerClosed(view);
+ getSupportActionBar().setDisplayShowTitleEnabled(true);
+ getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ initFragmentsWithFile();
+ invalidateOptionsMenu();
+ }
+
+ /** Called when a drawer has settled in a completely open state. */
+ public void onDrawerOpened(View drawerView) {
+ super.onDrawerOpened(drawerView);
+ getSupportActionBar().setTitle(R.string.drawer_open);
+ getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ invalidateOptionsMenu();
+ }
+ };
+
+ mDrawerToggle.setDrawerIndicatorEnabled(true);
+
+ // Notification Drawer
+ LinearLayout notificatonDrawer = (LinearLayout) findViewById(R.id.left_drawer);
+
+ // ListView
+ ListView listView = (ListView) notificatonDrawer.findViewById(R.id.drawer_list);
+ final NavigationDrawerListAdapter adapter = new NavigationDrawerListAdapter(getApplicationContext(), this);
+
+ listView.setAdapter(adapter);
+
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (showAccounts && position > 0){
+ position = position - 1;
+ }
+ switch (position){
+ case 0:
+ showAccounts = !showAccounts;
+ adapter.setShowAccounts(showAccounts);
+ adapter.notifyDataSetChanged();
+ break;
+ case 1:
+ MainApp.showOnlyFilesOnDevice(false);
+ mDrawerLayout.closeDrawers();
+ break;
+ case 2:
+ MainApp.showOnlyFilesOnDevice(true);
+ mDrawerLayout.closeDrawers();
+ break;
+ case 3:
+ Intent settingsIntent = new Intent(getApplicationContext(), Preferences.class);
+ startActivity(settingsIntent);
+ break;
+ }
+ }
+ });
+
+ // User-Icon
+ ImageView userIcon = (ImageView) notificatonDrawer.findViewById(R.id.drawer_userIcon);
+ userIcon.setImageResource(DisplayUtils.getSeasonalIconId());
+
+ // Username
+ TextView username = (TextView) notificatonDrawer.findViewById(R.id.drawer_username);
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
+ int lastAtPos = account.name.lastIndexOf("@");
+ username.setText(account.name.substring(0, lastAtPos));
+
+ // Set the drawer toggle as the DrawerListener
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
mDualPane = getResources().getBoolean(R.bool.large_land_layout);
mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
mRightFragmentContainer = findViewById(R.id.right_fragment_container);
// Action bar setup
mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
+ getSupportActionBar().setDisplayShowCustomEnabled(true); // CRUCIAL - for displaying your custom actionbar
+ getSupportActionBar().setDisplayShowTitleEnabled(true);
setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/); // always AFTER setContentView(...) ; to work around bug in its implementation
+ mDrawerToggle.syncState();
+
setBackgroundText();
- Log_OC.d(TAG, "onCreate() end");
+ Log_OC.v(TAG, "onCreate() end");
}
@Override
protected void onStart() {
+ Log_OC.v(TAG, "onStart() start");
super.onStart();
getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
+ Log_OC.v(TAG, "onStart() end");
}
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
@Override
protected void onDestroy() {
+ Log_OC.v(TAG, "onDestroy() start");
super.onDestroy();
+ Log_OC.v(TAG, "onDestroy() end");
}
/**
setNavigationListWithFolder(file);
if (!stateWasRecovered) {
- Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+ Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..");
initFragmentsWithFile();
if (file.isFolder()) {
startSyncFolderOperation(file, false);
/// First fragment
OCFileListFragment listOfFiles = getListOfFilesFragment();
if (listOfFiles != null) {
- listOfFiles.listDirectory(getCurrentDir());
+ listOfFiles.listDirectory(getCurrentDir(), MainApp.getOnlyOnDevice());
} else {
Log_OC.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
}
protected void refreshListOfFilesFragment() {
OCFileListFragment fileListFragment = getListOfFilesFragment();
if (fileListFragment != null) {
- fileListFragment.listDirectory();
+ fileListFragment.listDirectory(MainApp.getOnlyOnDevice());
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- if (BuildConfig.DEBUG) {
- menu.findItem(R.id.action_logger).setVisible(true);
- } else {
- menu.findItem(R.id.action_logger).setVisible(false);
- }
+ 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);
+
return super.onPrepareOptionsMenu(menu);
}
inflater.inflate(R.menu.main_menu, menu);
return true;
}
+
@Override
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(), "createdirdialog");
+ 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: {
- showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
+ UploadSourceDialogFragment dialog = UploadSourceDialogFragment.newInstance(getAccount());
+ dialog.show(getSupportFragmentManager(), DIALOG_UPLOAD_SOURCE);
+
break;
}
- case R.id.action_settings: {
- Intent settingsIntent = new Intent(this, Preferences.class);
- startActivity(settingsIntent);
- break;
- }
- case R.id.action_logger: {
- Intent loggerIntent = new Intent(getApplicationContext(),LogHistoryActivity.class);
- startActivity(loggerIntent);
- break;
- }
case android.R.id.home: {
- FileFragment second = getSecondFragment();
- OCFile currentDir = getCurrentDir();
- if((currentDir != null && currentDir.getParentId() != 0) ||
- (second != null && second.getFile() != null)) {
- onBackPressed();
-
+ if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
+ mDrawerLayout.closeDrawer(GravityCompat.START);
+ } else {
+ mDrawerLayout.openDrawer(GravityCompat.START);
}
+ // TODO add hamburger to left of android.R.id.home
break;
}
case R.id.action_sort: {
// Read sorting order, default to sort by name ascending
Integer sortOrder = appPreferences
- .getInt("sortOrder", FileListListAdapter.SORT_NAME);
+ .getInt("sortOrder", FileStorageUtils.SORT_NAME);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.actionbar_sort_title)
sortByDate(false);
break;
- // TODO re-enable when server-side folder size calculation is available
- // case 2:
- // sortBySize(false);
- // break;
}
dialog.dismiss();
}
private void startSynchronization() {
- Log_OC.e(TAG, "Got to start sync");
+ Log_OC.d(TAG, "Got to start sync");
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
- Log_OC.e(TAG, "Canceling all syncs for " + MainApp.getAuthority());
+ Log_OC.d(TAG, "Canceling all syncs for " + MainApp.getAuthority());
ContentResolver.cancelSync(null, MainApp.getAuthority()); // cancel the current synchronizations of any ownCloud account
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
+ Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
ContentResolver.requestSync(
getAccount(),
MainApp.getAuthority(), bundle);
} else {
- Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
+ Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
SyncRequest.Builder builder = new SyncRequest.Builder();
builder.setSyncAdapter(getAccount(), MainApp.getAuthority());
builder.setExpedited(true);
*
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
//getClipData is only supported on api level 16+, Jelly Bean
},
DELAY_TO_REQUEST_OPERATION_ON_ACTIVITY_RESULTS
);
+
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
}
+
}
private void requestMultipleUpload(Intent data, int resultCode) {
if (filePaths != null) {
String[] remotePaths = new String[filePaths.length];
String remotePathBase = "";
+
for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
}
private void requestSimpleUpload(Intent data, int resultCode) {
- String filepath = null;
+ String filePath = null;
String mimeType = null;
Uri selectedImageUri = data.getData();
try {
mimeType = getContentResolver().getType(selectedImageUri);
- String filemanagerstring = selectedImageUri.getPath();
- String selectedImagePath = getPath(selectedImageUri);
+ String fileManagerString = selectedImageUri.getPath();
+ String selectedImagePath = UriUtils.getLocalPath(selectedImageUri, this);
if (selectedImagePath != null)
- filepath = selectedImagePath;
+ filePath = selectedImagePath;
else
- filepath = filemanagerstring;
+ filePath = fileManagerString;
} catch (Exception e) {
Log_OC.e(TAG, "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
- e.printStackTrace();
} finally {
- if (filepath == null) {
- Log_OC.e(TAG, "Couldnt resolve path to file");
- Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
+ if (filePath == null) {
+ Log_OC.e(TAG, "Couldn't resolve path to file");
+ Toast t = Toast.makeText(
+ this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG
+ );
t.show();
return;
}
}
Intent i = new Intent(this, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT,
- getAccount());
- String remotepath = new String();
- for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
- remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
- }
- if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
- remotepath += OCFile.PATH_SEPARATOR;
-
- if (filepath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
-
- Cursor cursor = MainApp.getAppContext().getContentResolver()
- .query(Uri.parse(filepath), null, null, null, null, null);
+ i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+ OCFile currentDir = getCurrentDir();
+ String remotePath = (currentDir != null) ? currentDir.getRemotePath() : OCFile.ROOT_PATH;
+ if (filePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
+ Cursor cursor = getContentResolver().query(Uri.parse(filePath), null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
- String displayName = cursor.getString(
- cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
- Log.i(TAG, "Display Name: " + displayName + "; mimeType: " + mimeType);
+ String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ Log_OC.v(TAG, "Display Name: " + displayName );
displayName.replace(File.separatorChar, '_');
displayName.replace(File.pathSeparatorChar, '_');
- remotepath += displayName + DisplayUtils.getComposedFileExtension(filepath);
+ remotePath += displayName + DisplayUtils.getComposedFileExtension(filePath);
}
+ // and what happens in case of error?; wrong target name for the upload
} finally {
cursor.close();
}
} else {
- remotepath += new File(filepath).getName();
+ remotePath += new File(filePath).getName();
}
- i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, filePath);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePath);
i.putExtra(FileUploader.KEY_MIME_TYPE, mimeType);
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
- i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+ i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
startService(i);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
- Log_OC.e(TAG, "onSaveInstanceState() start");
+ Log_OC.v(TAG, "onSaveInstanceState() start");
super.onSaveInstanceState(outState);
outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
//outState.putBoolean(FileDisplayActivity.KEY_REFRESH_SHARES_IN_PROGRESS, mRefreshSharesInProgress);
outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_SEND, mWaitingToSend);
- Log_OC.d(TAG, "onSaveInstanceState() end");
+ Log_OC.v(TAG, "onSaveInstanceState() end");
}
@Override
protected void onResume() {
+ Log_OC.v(TAG, "onResume() start");
super.onResume();
- Log_OC.e(TAG, "onResume() start");
-
+
// refresh list of files
refreshListOfFilesFragment();
IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
- syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
- syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
+ syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
mSyncBroadcastReceiver = new SyncBroadcastReceiver();
registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
//LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
mDownloadFinishReceiver = new DownloadFinishReceiver();
registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
- Log_OC.d(TAG, "onResume() end");
+ Log_OC.v(TAG, "onResume() end");
}
@Override
protected void onPause() {
- Log_OC.e(TAG, "onPause() start");
+ Log_OC.v(TAG, "onPause() start");
if (mSyncBroadcastReceiver != null) {
unregisterReceiver(mSyncBroadcastReceiver);
//LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
mDownloadFinishReceiver = null;
}
-
- Log_OC.d(TAG, "onPause() end");
super.onPause();
- }
-
-
- @Override
- protected Dialog onCreateDialog(int id) {
- Dialog dialog = null;
- AlertDialog.Builder builder;
- switch (id) {
- case DIALOG_SHORT_WAIT: {
- ProgressDialog working_dialog = new ProgressDialog(this);
- working_dialog.setMessage(getResources().getString(
- R.string.wait_a_moment));
- working_dialog.setIndeterminate(true);
- working_dialog.setCancelable(false);
- dialog = working_dialog;
- break;
- }
- case DIALOG_CHOOSE_UPLOAD_SOURCE: {
-
-
- String[] allTheItems = { getString(R.string.actionbar_upload_files),
- getString(R.string.actionbar_upload_from_apps) };
-
- builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.actionbar_upload);
- builder.setItems(allTheItems, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- if (item == 0) {
- // if (!mDualPane) {
- Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
- action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT, FileDisplayActivity.this.getAccount());
- startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
- // } else {
- // TODO create and handle new fragment
- // LocalFileListFragment
- // }
- } else if (item == 1) {
- 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);
- }
- }
- });
- dialog = builder.create();
- break;
- }
- case DIALOG_CERT_NOT_SAVED: {
- builder = new AlertDialog.Builder(this);
- builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
- builder.setCancelable(false);
- builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- };
- });
- dialog = builder.create();
- break;
- }
- default:
- dialog = null;
- }
-
- return dialog;
- }
-
- /**
- * Translates a content URI of an content to a physical path on the disk
- *
- * @param uri The URI to resolve
- * @return The path to the content or null if it could not be found
- */
- public String getPath(Uri uri) {
- final boolean isKitKatOrLater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
-
- // DocumentProvider
- if (isKitKatOrLater && DocumentsContract.isDocumentUri(getApplicationContext(), uri)) {
- // ExternalStorageProvider
- if (UriUtils.isExternalStorageDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- if ("primary".equalsIgnoreCase(type)) {
- return Environment.getExternalStorageDirectory() + "/" + split[1];
- }
- }
- // DownloadsProvider
- else if (UriUtils.isDownloadsDocument(uri)) {
-
- final String id = DocumentsContract.getDocumentId(uri);
- final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
- Long.valueOf(id));
-
- return UriUtils.getDataColumn(getApplicationContext(), contentUri, null, null);
- }
- // MediaProvider
- else if (UriUtils.isMediaDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- Uri contentUri = null;
- if ("image".equals(type)) {
- contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- } else if ("video".equals(type)) {
- contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
- } else if ("audio".equals(type)) {
- contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
- }
-
- final String selection = "_id=?";
- final String[] selectionArgs = new String[] { split[1] };
-
- return UriUtils.getDataColumn(getApplicationContext(), contentUri, selection, selectionArgs);
- }
- // Documents providers returned as content://...
- else if (UriUtils.isContentDocument(uri)) {
- return uri.toString();
- }
- }
- // MediaStore (and general)
- else if ("content".equalsIgnoreCase(uri.getScheme())) {
-
- // Return the remote address
- if (UriUtils.isGooglePhotosUri(uri))
- return uri.getLastPathSegment();
-
- return UriUtils.getDataColumn(getApplicationContext(), uri, null, null);
- }
- // File
- else if ("file".equalsIgnoreCase(uri.getScheme())) {
- return uri.getPath();
- }
- return null;
+ Log_OC.v(TAG, "onPause() end");
}
/**
if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
OCFileListFragment fileListFragment = getListOfFilesFragment();
if (fileListFragment != null) {
- fileListFragment.listDirectory(currentDir);
+ fileListFragment.listDirectory(currentDir, MainApp.getOnlyOnDevice());
}
}
setFile(currentFile);
}
- mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
+ mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
- if (SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
+ if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
equals(event) &&
/// TODO refactor and make common
synchResult != null && !synchResult.isSuccess() &&
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
- OwnCloudClient client = null;
+
try {
- OwnCloudAccount ocAccount =
+ OwnCloudClient client;
+ OwnCloudAccount ocAccount =
new OwnCloudAccount(getAccount(), context);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
- // TODO get rid of these exceptions
- } catch (AccountNotFoundException e) {
- e.printStackTrace();
- } catch (AuthenticatorException e) {
- e.printStackTrace();
- } catch (OperationCanceledException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- if (client != null) {
- OwnCloudCredentials cred = client.getCredentials();
- if (cred != null) {
- AccountManager am = AccountManager.get(context);
- if (cred.authTokenExpires()) {
- am.invalidateAuthToken(
- getAccount().type,
- cred.getAuthToken()
- );
- } else {
- am.clearPassword(getAccount());
+
+ if (client != null) {
+ OwnCloudCredentials cred = client.getCredentials();
+ if (cred != null) {
+ AccountManager am = AccountManager.get(context);
+ if (cred.authTokenExpires()) {
+ am.invalidateAuthToken(
+ getAccount().type,
+ cred.getAuthToken()
+ );
+ } else {
+ am.clearPassword(getAccount());
+ }
}
}
+ requestCredentialsUpdate();
+
+ } catch (AccountNotFoundException e) {
+ Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
}
-
- requestCredentialsUpdate();
-
+
}
}
removeStickyBroadcast(intent);
/**
- * Class waiting for broadcast events from the {@link FielDownloader} service.
+ * Class waiting for broadcast events from the {@link FileDownloader} service.
*
* Updates the UI when a download is started or finished, provided that it is relevant for the
* current folder.
*/
private class DownloadFinishReceiver extends BroadcastReceiver {
+
+ //int refreshCounter = 0;
@Override
public void onReceive(Context context, Intent intent) {
try {
boolean sameAccount = isSameAccount(context, intent);
String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
boolean isDescendant = isDescendant(downloadedRemotePath);
-
+
if (sameAccount && isDescendant) {
- refreshListOfFilesFragment();
- refreshSecondFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
+ String linkedToRemotePath = intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
+ if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
+ //Log_OC.v(TAG, "refresh #" + ++refreshCounter);
+ refreshListOfFilesFragment();
+ }
+ refreshSecondFragment(
+ intent.getAction(),
+ downloadedRemotePath,
+ intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false)
+ );
}
if (mWaitingToSend != null) {
- mWaitingToSend = getStorageManager().getFileByPath(mWaitingToSend.getRemotePath()); // Update the file to send
+ mWaitingToSend = getStorageManager().getFileByPath(mWaitingToSend.getRemotePath());
if (mWaitingToSend.isDown()) {
sendDownloadedFile();
}
private boolean isDescendant(String downloadedRemotePath) {
OCFile currentDir = getCurrentDir();
- return (currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()));
+ return (
+ currentDir != null &&
+ downloadedRemotePath != null &&
+ downloadedRemotePath.startsWith(currentDir.getRemotePath())
+ );
+ }
+
+ private boolean isAscendant(String linkedToRemotePath) {
+ OCFile currentDir = getCurrentDir();
+ return (
+ currentDir != null &&
+ currentDir.getRemotePath().startsWith(linkedToRemotePath)
+ );
}
private boolean isSameAccount(Context context, Intent intent) {
popDirname();
}
OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
- listOfFiles.listDirectory(root);
+ listOfFiles.listDirectory(root, MainApp.getOnlyOnDevice());
setFile(listOfFiles.getCurrentFile());
startSyncFolderOperation(root, false);
}
OCFileListFragment listOfFiles = getListOfFilesFragment();
if (listOfFiles != null) {
setNavigationListWithFolder(folder);
- listOfFiles.listDirectory(folder);
+ listOfFiles.listDirectory(folder, MainApp.getOnlyOnDevice());
setFile(listOfFiles.getCurrentFile());
startSyncFolderOperation(folder, false);
} else {
* TODO
*/
private void updateNavigationElementsInActionBar(OCFile chosenFile) {
- ActionBar actionBar = getSupportActionBar();
+ ActionBar actionBar = getSupportActionBar();
+
+ // For adding content description tag to a title field in the action bar
+ int actionBarTitleId = getResources().getIdentifier("action_bar_title", "id", "android");
+
if (chosenFile == null || mDualPane) {
// only list of files - set for browsing through folders
OCFile currentDir = getCurrentDir();
boolean noRoot = (currentDir != null && currentDir.getParentId() != 0);
- actionBar.setDisplayHomeAsUpEnabled(noRoot);
- actionBar.setDisplayShowTitleEnabled(!noRoot);
+// actionBar.setDisplayHomeAsUpEnabled(noRoot);
+// actionBar.setDisplayShowTitleEnabled(!noRoot);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
if (!noRoot) {
actionBar.setTitle(getString(R.string.default_display_name_for_root_folder));
+ View actionBarTitleView = getWindow().getDecorView().findViewById(actionBarTitleId);
+ if (actionBarTitleView != null) { // it's null in Android 2.x
+ actionBarTitleView.setContentDescription(getString(R.string.default_display_name_for_root_folder));
+ }
}
actionBar.setNavigationMode(!noRoot ? ActionBar.NAVIGATION_MODE_STANDARD : ActionBar.NAVIGATION_MODE_LIST);
actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(chosenFile.getFileName());
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ View actionBarTitleView = getWindow().getDecorView().findViewById(actionBarTitleId);
+ if (actionBarTitleView != null) { // it's null in Android 2.x
+ getWindow().getDecorView().findViewById(actionBarTitleId).
+ setContentDescription(chosenFile.getFileName());
+ }
}
}
// a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
OCFileListFragment listOfFiles = getListOfFilesFragment();
if (listOfFiles != null) {
- listOfFiles.listDirectory();
+ listOfFiles.listDirectory(MainApp.getOnlyOnDevice());
}
FileFragment secondFragment = getSecondFragment();
if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
}
};
-
-
- /**
- * Launch an intent to request the PIN code to the user before letting him use the app
- */
- private void requestPinCode() {
- boolean pinStart = false;
- SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- pinStart = appPrefs.getBoolean("set_pincode", false);
- if (pinStart) {
- Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
- i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
- startActivity(i);
- }
- }
-
-
@Override
public void onSavedCertificate() {
startSyncFolderOperation(getCurrentDir(), false);
@Override
public void onFailedSavingCertificate() {
- showDialog(DIALOG_CERT_NOT_SAVED);
+ ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
+ R.string.ssl_validator_not_saved, new String[]{}, R.string.common_ok, -1, -1
+ );
+ dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED);
}
@Override
private void requestForDownload() {
Account account = getAccount();
+ //if (!mWaitingToPreview.isDownloading()) {
if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
Intent i = new Intent(this, FileDownloader.class);
i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
if (file.isFolder()) {
return file;
} else if (getStorageManager() != null) {
- String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+ String parentPath = file.getRemotePath().substring(0,
+ file.getRemotePath().lastIndexOf(file.getFileName()));
return getStorageManager().getFileByPath(parentPath);
}
}
mSyncInProgress = true;
// perform folder synchronization
- RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
+ RemoteOperation synchFolderOp = new RefreshFolderOperation( folder,
currentSyncTime,
false,
getFileOperationsHelper().isSharedSupported(),
getAccount(),
getApplicationContext()
);
- synchFolderOp.execute(getAccount(), this, null, null);
+ synchFolderOp.execute(getAccount(), MainApp.getAppContext(), this, null, null);
setSupportProgressBarIndeterminateVisibility(true);
*/
public void showUntrustedCertDialog(RemoteOperationResult result) {
// Show a dialog with the certificate info
- SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError((CertificateCombinedException)result.getException());
+ SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError(
- (CertificateCombinedException)result.getException());
++ (CertificateCombinedException) result.getException());
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
dialog.show(ft, DIALOG_UNTRUSTED_CERT);
private void requestForDownload(OCFile file) {
Account account = getAccount();
- if (!mDownloaderBinder.isDownloading(account, file)) {
+ if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
Intent i = new Intent(this, FileDownloader.class);
i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
i.putExtra(FileDownloader.EXTRA_FILE, file);
private void sortByName(boolean ascending){
getListOfFilesFragment().sortByName(ascending);
}
+
+ public void restart(){
+ Intent i = new Intent(this, FileDisplayActivity.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(i);
+ }
+
+ public void closeDrawer() {
+ mDrawerLayout.closeDrawers();
+ }
+
}
- /* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
+ import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.CreateFolderOperation;
- import com.owncloud.android.operations.SynchronizeFolderOperation;
+ import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.fragment.FileFragment;
if (!stateWasRecovered) {
OCFileListFragment listOfFolders = getListOfFilesFragment();
- listOfFolders.listDirectory(folder);
+ listOfFolders.listDirectory(folder, false);
startSyncFolderOperation(folder, false);
}
mSyncInProgress = true;
// perform folder synchronization
- RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
+ RemoteOperation synchFolderOp = new RefreshFolderOperation( folder,
currentSyncTime,
false,
getFileOperationsHelper().isSharedSupported(),
IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
- syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
- syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
+ syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
mSyncBroadcastReceiver = new SyncBroadcastReceiver();
registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
MenuInflater inflater = getSherlock().getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
menu.findItem(R.id.action_upload).setVisible(false);
- menu.findItem(R.id.action_settings).setVisible(false);
- menu.findItem(R.id.action_sync_account).setVisible(false);
- menu.findItem(R.id.action_logger).setVisible(false);
menu.findItem(R.id.action_sort).setVisible(false);
return true;
}
protected void refreshListOfFilesFragment() {
OCFileListFragment fileListFragment = getListOfFilesFragment();
if (fileListFragment != null) {
- fileListFragment.listDirectory();
+ fileListFragment.listDirectory(false);
}
}
OCFileListFragment listOfFiles = getListOfFilesFragment();
if (listOfFiles != null) { // should never be null, indeed
OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
- listOfFiles.listDirectory(root);
+ listOfFiles.listDirectory(root, false);
setFile(listOfFiles.getCurrentFile());
updateNavigationElementsInActionBar();
startSyncFolderOperation(root, false);
equals(synchFolderRemotePath)) {
OCFileListFragment fileListFragment = getListOfFilesFragment();
if (fileListFragment != null) {
- fileListFragment.listDirectory(currentDir);
+ fileListFragment.listDirectory(currentDir, false);
}
}
setFile(currentFile);
}
mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) &&
- !SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
+ !RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
- if (SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
+ if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
equals(event) &&
/// TODO refactor and make common
synchResult != null && !synchResult.isSuccess() &&
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
- OwnCloudClient client = null;
try {
- OwnCloudAccount ocAccount =
+ OwnCloudClient client;
+ OwnCloudAccount ocAccount =
new OwnCloudAccount(getAccount(), context);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
- // TODO get rid of these exceptions
- } catch (AccountNotFoundException e) {
- e.printStackTrace();
- } catch (AuthenticatorException e) {
- e.printStackTrace();
- } catch (OperationCanceledException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- if (client != null) {
- OwnCloudCredentials cred = client.getCredentials();
- if (cred != null) {
- AccountManager am = AccountManager.get(context);
- if (cred.authTokenExpires()) {
- am.invalidateAuthToken(
- getAccount().type,
- cred.getAuthToken()
- );
- } else {
- am.clearPassword(getAccount());
+
+ if (client != null) {
+ OwnCloudCredentials cred = client.getCredentials();
+ if (cred != null) {
+ AccountManager am = AccountManager.get(context);
+ if (cred.authTokenExpires()) {
+ am.invalidateAuthToken(
+ getAccount().type,
+ cred.getAuthToken()
+ );
+ } else {
+ am.clearPassword(getAccount());
+ }
}
}
+ requestCredentialsUpdate();
+
+ } catch (AccountNotFoundException e) {
+ Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
}
-
- requestCredentialsUpdate();
-
+
}
}
removeStickyBroadcast(intent);
- /* ownCloud Android client application
+ /**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
+ import android.content.ComponentName;
+ import android.content.Context;
import android.content.Intent;
+ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+ import android.os.IBinder;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.BuildConfig;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.authentication.AuthenticatorActivity;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
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.RadioButtonPreference;
import com.owncloud.android.utils.DisplayUtils;
/**
* An Activity that allows the user to change the application's settings.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
*/
- public class Preferences extends SherlockPreferenceActivity implements AccountManagerCallback<Boolean> {
+ public class Preferences extends SherlockPreferenceActivity
+ implements AccountManagerCallback<Boolean>, ComponentsGetter {
private static final String TAG = "OwnCloudPreferences";
private String mAccountName;
private boolean mShowContextMenu = false;
private String mUploadPath;
+ private PreferenceCategory mPrefInstantUploadCategory;
+ private Preference mPrefInstantUpload;
private Preference mPrefInstantUploadPath;
+ private Preference mPrefInstantUploadPathWiFi;
+ private Preference mPrefInstantVideoUpload;
private Preference mPrefInstantVideoUploadPath;
+ private Preference mPrefInstantVideoUploadPathWiFi;
private String mUploadVideoPath;
+ protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
+ protected FileUploader.FileUploaderBinder mUploaderBinder = null;
+ private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
+
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(R.string.actionbar_settings);
+ // For adding content description tag to a title field in the action bar
+ int actionBarTitleId = getResources().getIdentifier("action_bar_title", "id", "android");
+ View actionBarTitleView = getWindow().getDecorView().findViewById(actionBarTitleId);
+ if (actionBarTitleView != null) { // it's null in Android 2.x
+ getWindow().getDecorView().findViewById(actionBarTitleId).
+ setContentDescription(getString(R.string.actionbar_settings));
+ }
+
// Load the accounts category for adding the list of accounts
mAccountsPrefCategory = (PreferenceCategory) findPreference("accounts_category");
pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
- i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "preferences");
- i.putExtra(PinCodeActivity.EXTRA_NEW_STATE, newValue.toString());
+ Intent i = new Intent(getApplicationContext(), PassCodeActivity.class);
+ Boolean enable = (Boolean) newValue;
+ i.setAction(
+ enable.booleanValue() ? PassCodeActivity.ACTION_ENABLE : PassCodeActivity.ACTION_DISABLE
+ );
startActivity(i);
return true;
}
-
+ if (BuildConfig.DEBUG) {
+ Preference pLog = findPreference("log");
+ if (pLog != null ){
+ pLog.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent loggerIntent = new Intent(getApplicationContext(),LogHistoryActivity.class);
+ startActivity(loggerIntent);
+ return true;
+ }
+ });
+ }
+ }
+
boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
Preference pRecommend = findPreference("recommend");
if (pRecommend != null){
String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
- String recommendText = String.format(getString(R.string.recommend_text), appName, downloadUrl, username);
+ String recommendText = String.format(getString(R.string.recommend_text),
+ appName, downloadUrl, username);
intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
intent.putExtra(Intent.EXTRA_TEXT, recommendText);
startActivity(intent);
-
return(true);
}
}
});
}
-
+
+ mPrefInstantUploadCategory = (PreferenceCategory) findPreference("instant_uploading_category");
+
+ mPrefInstantUploadPathWiFi = findPreference("instant_upload_on_wifi");
+ mPrefInstantUpload = findPreference("instant_uploading");
+
+ toggleInstantPictureOptions(((CheckBoxPreference) mPrefInstantUpload).isChecked());
+
+ mPrefInstantUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ toggleInstantPictureOptions((Boolean) newValue);
+ return true;
+ }
+ });
+
mPrefInstantVideoUploadPath = findPreference("instant_video_upload_path");
if (mPrefInstantVideoUploadPath != null){
}
});
}
+
+ mPrefInstantVideoUploadPathWiFi = findPreference("instant_video_upload_on_wifi");
+ mPrefInstantVideoUpload = findPreference("instant_video_uploading");
+ toggleInstantVideoOptions(((CheckBoxPreference) mPrefInstantVideoUpload).isChecked());
+
+ mPrefInstantVideoUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ toggleInstantVideoOptions((Boolean) newValue);
+ return true;
+ }
+ });
/* About App */
pAboutApp = (Preference) findPreference("about_app");
loadInstantUploadPath();
loadInstantUploadVideoPath();
- }
+ /* ComponentsGetter */
+ mDownloadServiceConnection = newTransferenceServiceConnection();
+ if (mDownloadServiceConnection != null) {
+ bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+ mUploadServiceConnection = newTransferenceServiceConnection();
+ if (mUploadServiceConnection != null) {
+ bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
- @Override
- protected void onPause() {
- super.onPause();
+ }
+
+ private void toggleInstantPictureOptions(Boolean value){
+ if (value){
+ mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPathWiFi);
+ mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPath);
+ } else {
+ mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi);
+ mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath);
+ }
+ }
+
+ private void toggleInstantVideoOptions(Boolean value){
+ if (value){
+ mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPathWiFi);
+ mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPath);
+ } else {
+ mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi);
+ mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath);
+ }
}
@Override
// Remove account
am.removeAccount(a, this, mHandler);
+ Log_OC.d(TAG, "Remove an account " + a.name);
}
}
}
@Override
public void run(AccountManagerFuture<Boolean> future) {
if (future.isDone()) {
+ // after remove account
+ Account account = new Account(mAccountName, MainApp.getAccountType());
+ if (!AccountUtils.exists(account, MainApp.getAppContext())) {
+ // Cancel tranfers
+ if (mUploaderBinder != null) {
+ mUploaderBinder.cancel(account);
+ }
+ if (mDownloaderBinder != null) {
+ mDownloaderBinder.cancel(account);
+ }
+ }
+
Account a = AccountUtils.getCurrentOwnCloudAccount(this);
String accountName = "";
if (a == null) {
@Override
protected void onDestroy() {
mDbHandler.close();
+
+ if (mDownloadServiceConnection != null) {
+ unbindService(mDownloadServiceConnection);
+ mDownloadServiceConnection = null;
+ }
+ if (mUploadServiceConnection != null) {
+ unbindService(mUploadServiceConnection);
+ mUploadServiceConnection = null;
+ }
+
super.onDestroy();
}
FileDisplayActivity.class
);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);
} else {
finish();
editor.putString("instant_video_upload_path", mUploadVideoPath);
editor.commit();
}
+
+ // Methods for ComponetsGetter
+ @Override
+ public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
+ return mDownloaderBinder;
+ }
+
+
+ @Override
+ public FileUploader.FileUploaderBinder getFileUploaderBinder() {
+ return mUploaderBinder;
+ }
+
+ @Override
+ public OperationsService.OperationsServiceBinder getOperationsServiceBinder() {
+ return null;
+ }
+
+ @Override
+ public FileDataStorageManager getStorageManager() {
+ return null;
+ }
+
+ @Override
+ public FileOperationsHelper getFileOperationsHelper() {
+ return null;
+ }
+
+ protected ServiceConnection newTransferenceServiceConnection() {
+ return new PreferencesServiceConnection();
+ }
+
+ /** Defines callbacks for service binding, passed to bindService() */
+ private class PreferencesServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+
+ 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
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
+ Log_OC.d(TAG, "Download service suddenly disconnected");
+ mDownloaderBinder = null;
+ } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service suddenly disconnected");
+ mUploaderBinder = null;
+ }
+ }
+ };
}
- /* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
package com.owncloud.android.ui.activity;
import android.accounts.Account;
-
import android.os.Bundle;
import android.view.View.OnClickListener;
if (!stateWasRecovered) {
OCFileListFragment listOfFolders = getListOfFilesFragment();
- listOfFolders.listDirectory(folder);
+ listOfFolders.listDirectory(folder, false);
startSyncFolderOperation(folder, false);
}
- /* ownCloud Android client application
+ /**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author masensio
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
import java.util.Stack;
import java.util.Vector;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.AlertDialog;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
+ import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
+ import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
+ import android.support.v4.app.Fragment;
+ import android.support.v4.app.FragmentManager;
+ import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
+ import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
- import com.actionbarsherlock.app.SherlockListActivity;
import com.actionbarsherlock.view.MenuItem;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.utils.Log_OC;
++import com.owncloud.android.MainApp;
++import com.owncloud.android.R;
++import com.owncloud.android.authentication.AccountAuthenticator;
+ import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.files.services.FileUploader;
+ import com.owncloud.android.lib.common.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.operations.CreateFolderOperation;
+ import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
+ import com.owncloud.android.ui.dialog.LoadingDialog;
+ import com.owncloud.android.utils.CopyTmpFileAsyncTask;
import com.owncloud.android.utils.DisplayUtils;
+ import com.owncloud.android.utils.ErrorMessageAdapter;
+
/**
* This can be used to upload things to an ownCloud instance.
- *
- * @author Bartek Przybylski
- *
*/
- public class Uploader extends SherlockListActivity implements OnItemClickListener, android.view.View.OnClickListener {
- private static final String TAG = "ownCloudUploader";
+ public class Uploader extends FileActivity
+ implements OnItemClickListener, android.view.View.OnClickListener,
+ CopyTmpFileAsyncTask.OnCopyTmpFileTaskListener {
+
+ private static final String TAG = Uploader.class.getSimpleName();
- private Account mAccount;
private AccountManager mAccountManager;
private Stack<String> mParents;
private ArrayList<Parcelable> mStreamsToUpload;
private boolean mCreateDir;
private String mUploadPath;
- private FileDataStorageManager mStorageManager;
private OCFile mFile;
+ private boolean mAccountSelected;
+ private boolean mAccountSelectionShowing;
+ private ArrayList<String> mRemoteCacheData;
+ private int mNumCacheFile;
+
private final static int DIALOG_NO_ACCOUNT = 0;
private final static int DIALOG_WAITING = 1;
private final static int DIALOG_NO_STREAM = 2;
private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
+ private final static String KEY_PARENTS = "PARENTS";
+ private final static String KEY_FILE = "FILE";
+ private final static String KEY_ACCOUNT_SELECTED = "ACCOUNT_SELECTED";
+ private final static String KEY_ACCOUNT_SELECTION_SHOWING = "ACCOUNT_SELECTION_SHOWING";
+ private final static String KEY_NUM_CACHE_FILE = "NUM_CACHE_FILE";
+ private final static String KEY_REMOTE_CACHE_DATA = "REMOTE_CACHE_DATA";
+
+ private static final String DIALOG_WAIT_COPY_FILE = "DIALOG_WAIT_COPY_FILE";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
+ prepareStreamsToUpload();
+
+ if (savedInstanceState == null) {
+ mParents = new Stack<String>();
+ mAccountSelected = false;
+ mAccountSelectionShowing = false;
+ mNumCacheFile = 0;
+
+ // ArrayList for files with path in private storage
+ mRemoteCacheData = new ArrayList<String>();
+ } else {
+ mParents = (Stack<String>) savedInstanceState.getSerializable(KEY_PARENTS);
+ mFile = savedInstanceState.getParcelable(KEY_FILE);
+ mAccountSelected = savedInstanceState.getBoolean(KEY_ACCOUNT_SELECTED);
+ mAccountSelectionShowing = savedInstanceState.getBoolean(KEY_ACCOUNT_SELECTION_SHOWING);
+ mNumCacheFile = savedInstanceState.getInt(KEY_NUM_CACHE_FILE);
+ mRemoteCacheData = savedInstanceState.getStringArrayList(KEY_REMOTE_CACHE_DATA);
+ }
+
super.onCreate(savedInstanceState);
- mParents = new Stack<String>();
+
+ if (mAccountSelected) {
+ setAccount((Account) savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT));
+ }
+
ActionBar actionBar = getSupportActionBar();
actionBar.setIcon(DisplayUtils.getSeasonalIconId());
- if (prepareStreamsToUpload()) {
+ }
+
+ @Override
+ protected void setAccount(Account account, boolean savedAccount) {
+ if (somethingToUpload()) {
mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
if (accounts.length == 0) {
Log_OC.i(TAG, "No ownCloud account is available");
showDialog(DIALOG_NO_ACCOUNT);
- } else if (accounts.length > 1) {
- Log_OC.i(TAG, "More then one ownCloud is available");
+ } else if (accounts.length > 1 && !mAccountSelected && !mAccountSelectionShowing) {
+ Log_OC.i(TAG, "More than one ownCloud is available");
showDialog(DIALOG_MULTIPLE_ACCOUNT);
+ mAccountSelectionShowing = true;
} else {
- mAccount = accounts[0];
- mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
- initTargetFolder();
- populateDirectoryList();
-
+ if (!savedAccount) {
+ setAccount(accounts[0]);
+ }
}
-
+
} else {
showDialog(DIALOG_NO_STREAM);
}
+
+ super.setAccount(account, savedAccount);
}
-
+
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(mAccountWasRestored);
+ initTargetFolder();
+ populateDirectoryList();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ Log_OC.d(TAG, "onSaveInstanceState() start");
+ super.onSaveInstanceState(outState);
+ outState.putSerializable(KEY_PARENTS, mParents);
+ //outState.putParcelable(KEY_ACCOUNT, mAccount);
+ outState.putParcelable(KEY_FILE, mFile);
+ outState.putBoolean(KEY_ACCOUNT_SELECTED, mAccountSelected);
+ outState.putBoolean(KEY_ACCOUNT_SELECTION_SHOWING, mAccountSelectionShowing);
+ outState.putInt(KEY_NUM_CACHE_FILE, mNumCacheFile);
+ outState.putStringArrayList(KEY_REMOTE_CACHE_DATA, mRemoteCacheData);
+ outState.putParcelable(FileActivity.EXTRA_ACCOUNT, getAccount());
+
+ Log_OC.d(TAG, "onSaveInstanceState() end");
+ }
+
@Override
protected Dialog onCreateDialog(final int id) {
final AlertDialog.Builder builder = new Builder(this);
case DIALOG_NO_ACCOUNT:
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(R.string.uploader_wrn_no_account_title);
- builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
+ builder.setMessage(String.format(
+ getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
builder.setCancelable(false);
builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
@Override
});
return builder.create();
case DIALOG_MULTIPLE_ACCOUNT:
- CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
+ CharSequence ac[] = new CharSequence[
+ mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
for (int i = 0; i < ac.length; ++i) {
- ac[i] = DisplayUtils.convertIdn(mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name, false);
+ ac[i] = DisplayUtils.convertIdn(
+ mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name, false);
}
builder.setTitle(R.string.common_choose_account);
builder.setItems(ac, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which];
- mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
- initTargetFolder();
- populateDirectoryList();
+ setAccount(mAccountManager.getAccountsByType(MainApp.getAccountType())[which]);
+ onAccountSet(mAccountWasRestored);
+ dialog.dismiss();
+ mAccountSelected = true;
+ mAccountSelectionShowing = false;
}
});
builder.setCancelable(true);
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
+ mAccountSelectionShowing = false;
dialog.cancel();
finish();
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// click on folder in the list
Log_OC.d(TAG, "on item click");
- Vector<OCFile> tmpfiles = mStorageManager.getFolderContent(mFile, false);
- Vector<OCFile> tmpfiles = getStorageManager().getFolderContent(mFile);
++ Vector<OCFile> tmpfiles = getStorageManager().getFolderContent(mFile, false);
if (tmpfiles.size() <= 0) return;
// filter on dirtype
Vector<OCFile> files = new Vector<OCFile>();
// click on button
switch (v.getId()) {
case R.id.uploader_choose_folder:
- mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
+ mUploadPath = ""; // first element in mParents is root dir, represented by "";
+ // init mUploadPath with "/" results in a "//" prefix
for (String p : mParents)
mUploadPath += p + OCFile.PATH_SEPARATOR;
Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
uploadFiles();
break;
+
+ case R.id.uploader_new_folder:
+ CreateFolderDialogFragment dialog = CreateFolderDialogFragment.newInstance(mFile);
+ dialog.show(getSupportFragmentManager(), "createdirdialog");
+ break;
+
+
default:
throw new IllegalArgumentException("Wrong element clicked");
}
// there is no need for checking for is there more then one
// account at this point
// since account setup can set only one account at time
- mAccount = accounts[0];
+ setAccount(accounts[0]);
populateDirectoryList();
}
}
private void populateDirectoryList() {
setContentView(R.layout.uploader_layout);
+
+ ListView mListView = (ListView) findViewById(android.R.id.list);
String current_dir = mParents.peek();
if(current_dir.equals("")){
actionBar.setHomeButtonEnabled(notRoot);
String full_path = generatePath(mParents);
-
+
Log_OC.d(TAG, "Populating view with content of : " + full_path);
- mFile = mStorageManager.getFileByPath(full_path);
+ mFile = getStorageManager().getFileByPath(full_path);
if (mFile != null) {
- Vector<OCFile> files = mStorageManager.getFolderContent(mFile, false);
- Vector<OCFile> files = getStorageManager().getFolderContent(mFile);
++ Vector<OCFile> files = getStorageManager().getFolderContent(mFile, false);
List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
for (OCFile f : files) {
HashMap<String, Object> h = new HashMap<String, Object>();
data,
R.layout.uploader_list_item_layout,
new String[] {"dirname"},
- new int[] {R.id.drawer_username});
- setListAdapter(sa);
- Button btn = (Button) findViewById(R.id.uploader_choose_folder);
- btn.setOnClickListener(this);
- getListView().setOnItemClickListener(this);
+ new int[] {R.id.textView1});
+
+ mListView.setAdapter(sa);
+ Button btnChooseFolder = (Button) findViewById(R.id.uploader_choose_folder);
+ btnChooseFolder.setOnClickListener(this);
+
+ Button btnNewFolder = (Button) findViewById(R.id.uploader_new_folder);
+ btnNewFolder.setOnClickListener(this);
+
+ mListView.setOnItemClickListener(this);
}
}
return full_path;
}
- private boolean prepareStreamsToUpload() {
+ private void prepareStreamsToUpload() {
if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
mStreamsToUpload = new ArrayList<Parcelable>();
mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
} else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
}
+ }
+
+ private boolean somethingToUpload() {
return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
}
public void uploadFiles() {
try {
+ // ArrayList for files with path in external storage
ArrayList<String> local = new ArrayList<String>();
ArrayList<String> remote = new ArrayList<String>();
for (Parcelable mStream : mStreamsToUpload) {
Uri uri = (Uri) mStream;
- if (uri !=null) {
+ String data = null;
+ String filePath = "";
+
+ if (uri != null) {
if (uri.getScheme().equals("content")) {
-
- String mimeType = getContentResolver().getType(uri);
-
- if (mimeType.contains("image")) {
- String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Images.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
-
- }
- else if (mimeType.contains("video")) {
- String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Video.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
+ String mimeType = getContentResolver().getType(uri);
+
+ if (mimeType.contains("image")) {
+ String[] CONTENT_PROJECTION = { Images.Media.DATA,
+ Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE,
+ Images.Media.SIZE };
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
+ null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Images.Media.DATA);
+ data = c.getString(index);
+ filePath = mUploadPath +
+ c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
+
+ } else if (mimeType.contains("video")) {
+ String[] CONTENT_PROJECTION = { Video.Media.DATA,
+ Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE,
+ Video.Media.SIZE, Video.Media.DATE_MODIFIED };
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
+ null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Video.Media.DATA);
+ data = c.getString(index);
+ filePath = mUploadPath +
+ c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME));
- }
- else if (mimeType.contains("audio")) {
- String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Audio.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
-
- }
- else {
- String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
- // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
- if (filePath.contains("mnt")) {
- String splitedFilePath[] = filePath.split("/mnt");
- filePath = splitedFilePath[1];
- }
- final File file = new File(filePath);
- local.add(file.getAbsolutePath());
- remote.add(mUploadPath + file.getName());
- }
-
+ } else if (mimeType.contains("audio")) {
+ String[] CONTENT_PROJECTION = { Audio.Media.DATA,
+ Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE,
+ Audio.Media.SIZE };
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
+ null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Audio.Media.DATA);
+ data = c.getString(index);
+ filePath = mUploadPath +
+ c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME));
+
+ } else {
+ Cursor cursor = getContentResolver().query(uri,
+ new String[]{MediaStore.MediaColumns.DISPLAY_NAME},
+ null, null, null);
+ cursor.moveToFirst();
+ int nameIndex = cursor.getColumnIndex(cursor.getColumnNames()[0]);
+ if (nameIndex >= 0) {
+ filePath = mUploadPath + cursor.getString(nameIndex);
+ }
+ }
+
} else if (uri.getScheme().equals("file")) {
- String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
+ filePath = Uri.decode(uri.toString()).replace(uri.getScheme() +
+ "://", "");
if (filePath.contains("mnt")) {
String splitedFilePath[] = filePath.split("/mnt");
filePath = splitedFilePath[1];
}
final File file = new File(filePath);
- local.add(file.getAbsolutePath());
- remote.add(mUploadPath + file.getName());
+ data = file.getAbsolutePath();
+ filePath = mUploadPath + file.getName();
}
else {
throw new SecurityException();
}
+ if (data == null) {
+ mRemoteCacheData.add(filePath);
+ CopyTmpFileAsyncTask copyTask = new CopyTmpFileAsyncTask(this);
+ Object[] params = { uri, filePath, mRemoteCacheData.size()-1,
+ getAccount().name, getContentResolver()};
+ mNumCacheFile++;
+ showWaitingCopyDialog();
+ copyTask.execute(params);
+ } else {
+ remote.add(filePath);
+ local.add(data);
+ }
}
else {
throw new SecurityException();
}
-
- Intent intent = new Intent(getApplicationContext(), FileUploader.class);
- intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
- intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
- intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
- intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
- startService(intent);
- //Save the path to shared preferences
- SharedPreferences.Editor appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- appPrefs.putString("last_upload_path", mUploadPath);
- appPrefs.apply();
+ Intent intent = new Intent(getApplicationContext(), FileUploader.class);
+ intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+ intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
+ intent.putExtra(FileUploader.KEY_REMOTE_FILE,
+ remote.toArray(new String[remote.size()]));
+ intent.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+ startService(intent);
+
+ //Save the path to shared preferences
+ SharedPreferences.Editor appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+ appPrefs.putString("last_upload_path", mUploadPath);
+ appPrefs.apply();
- finish();
+ finish();
}
} catch (SecurityException e) {
- String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
+ String message = String.format(getString(R.string.uploader_error_forbidden_content),
+ getString(R.string.app_name));
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ super.onRemoteOperationFinish(operation, result);
+
+
+ if (operation instanceof CreateFolderOperation) {
+ onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
+ }
+
+ }
+
+ /**
+ * Updates the view associated to the activity after the finish of an operation
+ * trying create a new folder
+ *
+ * @param operation Creation operation performed.
+ * @param result Result of the creation.
+ */
+ private void onCreateFolderOperationFinish(CreateFolderOperation operation,
+ RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ dismissLoadingDialog();
+ populateDirectoryList();
+ } else {
+ dismissLoadingDialog();
+ try {
+ Toast msg = Toast.makeText(this,
+ ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+ Toast.LENGTH_LONG);
+ msg.show();
+
+ } catch (NotFoundException e) {
+ Log_OC.e(TAG, "Error while trying to show fail message " , e);
+ }
+ }
+ }
+
+
/**
* Loads the target folder initialize shown to the user.
*
* The target account has to be chosen before this method is called.
*/
private void initTargetFolder() {
- if (mStorageManager == null) {
- throw new IllegalStateException("Do not call this method before initializing mStorageManager");
+ if (getStorageManager() == null) {
+ throw new IllegalStateException("Do not call this method before " +
+ "initializing mStorageManager");
}
SharedPreferences appPreferences = PreferenceManager
// "/" equals root-directory
if(last_path.equals("/")) {
mParents.add("");
- }
- else{
+ } else{
String[] dir_names = last_path.split("/");
for (String dir : dir_names)
mParents.add(dir);
}
//Make sure that path still exists, if it doesn't pop the stack and try the previous path
- while(!mStorageManager.fileExists(generatePath(mParents)) && mParents.size() > 1){
- mParents.pop();
- }
+ while(!getStorageManager().fileExists(generatePath(mParents)) && mParents.size() > 1){
+ mParents.pop();
+ }
}
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
- case android.R.id.home: {
- if((mParents.size() > 1)) {
- onBackPressed();
- }
- break;
- }
- default:
- retval = super.onOptionsItemSelected(item);
+ case android.R.id.home:
+ if((mParents.size() > 1)) {
+ onBackPressed();
+ }
+ break;
+
+ default:
+ retval = super.onOptionsItemSelected(item);
}
return retval;
}
-
+
+ /**
+ * Process the result of CopyTmpFileAsyncTask
+ * @param result
+ * @param index
+ */
+ @Override
+ public void onTmpFileCopied(String result, int index) {
+ if (mNumCacheFile -- == 0) {
+ dismissWaitingCopyDialog();
+ }
+ if (result != null) {
+ Intent intent = new Intent(getApplicationContext(), FileUploader.class);
+ intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ intent.putExtra(FileUploader.KEY_LOCAL_FILE, result);
+ intent.putExtra(FileUploader.KEY_REMOTE_FILE, mRemoteCacheData.get(index));
+ intent.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+ startService(intent);
+
+ } else {
+ String message = String.format(getString(R.string.uploader_error_forbidden_content),
+ getString(R.string.app_name));
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ Log_OC.d(TAG, message);
+ }
+
+ }
+ /**
+ * Show waiting for copy dialog
+ */
+ public void showWaitingCopyDialog() {
+ // Construct dialog
+ LoadingDialog loading = new LoadingDialog(
+ getResources().getString(R.string.wait_for_tmp_copy_from_private_storage));
+ FragmentManager fm = getSupportFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ loading.show(ft, DIALOG_WAIT_COPY_FILE);
+
+ }
+
+
+ /**
+ * Dismiss waiting for copy dialog
+ */
+ public void dismissWaitingCopyDialog(){
+ Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_COPY_FILE);
+ if (frag != null) {
+ LoadingDialog loading = (LoadingDialog) frag;
+ loading.dismiss();
+ }
+ }
}
- /* ownCloud Android client application\r
+ /**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
+ * @author Tobias Kaminsky\r
+ * @author David A. Velasco\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2014 ownCloud Inc.\r
+ * Copyright (C) 2015 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License version 2,\r
*/\r
package com.owncloud.android.ui.adapter;\r
\r
-
+ \r
import java.io.File;\r
- import java.util.Collections;\r
- import java.util.Comparator;\r
import java.util.Vector;\r
\r
- import third_parties.daveKoeller.AlphanumComparator;\r
import android.accounts.Account;\r
import android.content.Context;\r
import android.content.SharedPreferences;\r
import android.graphics.Bitmap;\r
+ import android.os.Build;\r
import android.preference.PreferenceManager;\r
import android.text.format.DateUtils;\r
import android.view.LayoutInflater;\r
import android.view.View;\r
import android.view.ViewGroup;\r
+ import android.widget.AbsListView;\r
import android.widget.BaseAdapter;\r
import android.widget.ImageView;\r
+ import android.widget.LinearLayout;\r
import android.widget.ListAdapter;\r
- import android.widget.ListView;\r
import android.widget.TextView;\r
\r
import com.owncloud.android.R;\r
import com.owncloud.android.datamodel.FileDataStorageManager;\r
import com.owncloud.android.datamodel.OCFile;\r
import com.owncloud.android.datamodel.ThumbnailsCacheManager;\r
- import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable;\r
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
+ import com.owncloud.android.services.OperationsService.OperationsServiceBinder;\r
import com.owncloud.android.ui.activity.ComponentsGetter;\r
import com.owncloud.android.utils.DisplayUtils;\r
import com.owncloud.android.utils.FileStorageUtils;\r
-
+ \r
\r
/**\r
* This Adapter populates a ListView with all files and folders in an ownCloud\r
* instance.\r
- * \r
- * @author Bartek Przybylski\r
- * @author Tobias Kaminsky\r
- * @author David A. Velasco\r
*/\r
- public class FileListListAdapter extends BaseAdapter implements ListAdapter {
+ public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
private final static String PERMISSION_SHARED_WITH_ME = "S";\r
- \r
+ \r
private Context mContext;\r
private OCFile mFile = null;\r
private Vector<OCFile> mFiles = null;\r
+ private Vector<OCFile> mFilesOrig = new Vector<OCFile>();\r
private boolean mJustFolders;\r
\r
- private FileDataStorageManager mStorageManager;
- private Account mAccount;
+ private FileDataStorageManager mStorageManager;\r
+ private Account mAccount;\r
private ComponentsGetter mTransferServiceGetter;\r
- private Integer mSortOrder;\r
- public static final Integer SORT_NAME = 0;\r
- public static final Integer SORT_DATE = 1;\r
- public static final Integer SORT_SIZE = 2;\r
- private Boolean mSortAscending;\r
+ private boolean mGridMode;\r
+ \r
+ private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM };\r
+ \r
private SharedPreferences mAppPreferences;\r
\r
public FileListListAdapter(\r
boolean justFolders, \r
- Context context, \r
+ Context context,\r
ComponentsGetter transferServiceGetter\r
) {\r
- \r
+ \r
mJustFolders = justFolders;\r
mContext = context;\r
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
-
- mTransferServiceGetter = transferServiceGetter;
- \r
+ mTransferServiceGetter = transferServiceGetter;\r
+ \r
mAppPreferences = PreferenceManager\r
.getDefaultSharedPreferences(mContext);\r
\r
// Read sorting order, default to sort by name ascending\r
- mSortOrder = mAppPreferences\r
- .getInt("sortOrder", 0);\r
- mSortAscending = mAppPreferences.getBoolean("sortAscending", true);
+ FileStorageUtils.mSortOrder = mAppPreferences.getInt("sortOrder", 0);\r
+ FileStorageUtils.mSortAscending = mAppPreferences.getBoolean("sortAscending", true);\r
\r
// initialise thumbnails cache on background thread\r
new ThumbnailsCacheManager.InitDiskCacheTask().execute();\r
\r
+ mGridMode = false;\r
}\r
-
+ \r
@Override\r
public boolean areAllItemsEnabled() {\r
return true;\r
\r
@Override\r
public View getView(int position, View convertView, ViewGroup parent) {\r
+ \r
View view = convertView;\r
- if (view == null) {\r
- LayoutInflater inflator = (LayoutInflater) mContext\r
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
- view = inflator.inflate(R.layout.list_item, null);\r
- }\r
- \r
+ OCFile file = null;\r
+ LayoutInflater inflator = (LayoutInflater) mContext\r
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+ \r
if (mFiles != null && mFiles.size() > position) {\r
- OCFile file = mFiles.get(position);\r
- TextView fileName = (TextView) view.findViewById(R.id.Filename); \r
- String name = file.getFileName();\r
+ file = mFiles.get(position);\r
+ }\r
+ \r
+ // Find out which layout should be displayed\r
+ ViewType viewType;\r
+ if (!mGridMode){\r
+ viewType = ViewType.LIST_ITEM;\r
+ } else if (file.isImage()){\r
+ viewType = ViewType.GRID_IMAGE;\r
+ } else {\r
+ viewType = ViewType.GRID_ITEM;\r
+ }\r
+ \r
+ // create view only if differs, otherwise reuse\r
+ if (convertView == null || (convertView != null && convertView.getTag() != viewType)) {\r
+ switch (viewType) {\r
+ case GRID_IMAGE:\r
+ view = inflator.inflate(R.layout.grid_image, null);\r
+ view.setTag(ViewType.GRID_IMAGE);\r
+ break;\r
+ case GRID_ITEM:\r
+ view = inflator.inflate(R.layout.grid_item, null);\r
+ view.setTag(ViewType.GRID_ITEM);\r
+ break;\r
+ case LIST_ITEM:\r
+ view = inflator.inflate(R.layout.list_item, null);\r
+ view.setTag(ViewType.LIST_ITEM);\r
+ break;\r
+ }\r
+ }\r
+ \r
+ view.invalidate();\r
+ \r
+ if (file != null){\r
+ \r
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);\r
\r
- fileName.setText(name);\r
- ImageView fileIcon = (ImageView) view.findViewById(R.id.drawer_userIcon);\r
fileIcon.setTag(file.getFileId());\r
- ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);\r
- ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);\r
- sharedWithMeIconV.setVisibility(View.GONE);\r
- \r
- ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
- localStateView.bringToFront();\r
- FileDownloaderBinder downloaderBinder = \r
- mTransferServiceGetter.getFileDownloaderBinder();\r
- FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
- if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
- localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
- localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else if (file.isDown()) {\r
- localStateView.setImageResource(R.drawable.local_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else {\r
- localStateView.setVisibility(View.INVISIBLE);\r
+ TextView fileName;\r
+ String name = file.getFileName();\r
+ \r
+ LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.ListItemLayout);\r
+ linearLayout.setContentDescription("LinearLayout-" + name);\r
+ \r
+ switch (viewType){\r
+ case LIST_ITEM:\r
+ TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
+ TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
+ ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
+ \r
+ lastModV.setVisibility(View.VISIBLE);\r
+ lastModV.setText(showRelativeTimestamp(file));\r
+ \r
+ checkBoxV.setVisibility(View.GONE);\r
+ \r
+ fileSizeV.setVisibility(View.VISIBLE);\r
+ fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+ \r
+ if (!file.isFolder()) {\r
+ AbsListView parentList = (AbsListView)parent;\r
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\r
+ if (parentList.getChoiceMode() == AbsListView.CHOICE_MODE_NONE) {\r
+ checkBoxV.setVisibility(View.GONE);\r
+ } else {\r
+ if (parentList.isItemChecked(position)) {\r
+ checkBoxV.setImageResource(\r
+ android.R.drawable.checkbox_on_background);\r
+ } else {\r
+ checkBoxV.setImageResource(\r
+ android.R.drawable.checkbox_off_background);\r
+ }\r
+ checkBoxV.setVisibility(View.VISIBLE);\r
+ }\r
+ }\r
+ \r
+ } else { //Folder\r
+ fileSizeV.setVisibility(View.INVISIBLE);\r
+ }\r
+ \r
+ case GRID_ITEM:\r
+ // filename\r
+ fileName = (TextView) view.findViewById(R.id.Filename);\r
+ name = file.getFileName();\r
+ fileName.setText(name);\r
+ \r
+ case GRID_IMAGE:\r
+ // sharedIcon\r
+ ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);\r
+ if (file.isShareByLink()) {\r
+ sharedIconV.setVisibility(View.VISIBLE);\r
+ sharedIconV.bringToFront();\r
+ } else {\r
+ sharedIconV.setVisibility(View.GONE);\r
+ }\r
+ \r
+ // local state\r
+ ImageView localStateView = (ImageView) view.findViewById(R.id.localFileIndicator);\r
+ localStateView.bringToFront();\r
+ FileDownloaderBinder downloaderBinder =\r
+ mTransferServiceGetter.getFileDownloaderBinder();\r
+ FileUploaderBinder uploaderBinder =\r
+ mTransferServiceGetter.getFileUploaderBinder();\r
+ boolean downloading = (downloaderBinder != null &&\r
+ downloaderBinder.isDownloading(mAccount, file));\r
+ OperationsServiceBinder opsBinder =\r
+ mTransferServiceGetter.getOperationsServiceBinder();\r
+ downloading |= (opsBinder != null &&\r
+ opsBinder.isSynchronizing(mAccount, file.getRemotePath()));\r
+ if (downloading) {\r
+ localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else if (uploaderBinder != null &&\r
+ uploaderBinder.isUploading(mAccount, file)) {\r
+ localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else if (file.isDown()) {\r
+ localStateView.setImageResource(R.drawable.local_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else {\r
+ localStateView.setVisibility(View.INVISIBLE);\r
+ }\r
+ \r
+ // share with me icon\r
+ if (!file.isFolder()) {\r
+ ImageView sharedWithMeIconV = (ImageView)\r
+ view.findViewById(R.id.sharedWithMeIcon);\r
+ sharedWithMeIconV.bringToFront();\r
+ if (checkIfFileIsSharedWithMe(file)) {\r
+ sharedWithMeIconV.setVisibility(View.VISIBLE);\r
+ } else {\r
+ sharedWithMeIconV.setVisibility(View.GONE);\r
+ }\r
+ }\r
+ \r
+ break;\r
}\r
\r
- TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
- TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
- ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
+ // For all Views\r
+ \r
+ // this if-else is needed even though favorite icon is visible by default\r
+ // because android reuses views in listview\r
+ if (!file.keepInSync()) {\r
+ view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);\r
+ } else {\r
+ view.findViewById(R.id.favoriteIcon).setVisibility(View.VISIBLE);\r
+ }\r
\r
+ // No Folder\r
if (!file.isFolder()) {\r
- fileSizeV.setVisibility(View.VISIBLE);\r
- fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(showRelativeTimestamp(file));\r
- // this if-else is needed even thoe fav icon is visible by default\r
- // because android reuses views in listview\r
- if (!file.keepInSync()) {\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
- } else {\r
- view.findViewById(R.id.imageView3).setVisibility(View.VISIBLE);\r
- }\r
- \r
- ListView parentList = (ListView)parent;\r
- if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { \r
- checkBoxV.setVisibility(View.GONE);\r
- } else {\r
- if (parentList.isItemChecked(position)) {\r
- checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);\r
- } else {\r
- checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
- }\r
- checkBoxV.setVisibility(View.VISIBLE);\r
- } \r
- \r
- // get Thumbnail if file is image\r
if (file.isImage() && file.getRemoteId() != null){\r
- // Thumbnail in Cache?\r
+ // Thumbnail in Cache?\r
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(\r
String.valueOf(file.getRemoteId())\r
- );\r
+ );\r
if (thumbnail != null && !file.needsUpdateThumbnail()){\r
fileIcon.setImageBitmap(thumbnail);\r
} else {\r
// generate new Thumbnail\r
if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {\r
- final ThumbnailsCacheManager.ThumbnailGenerationTask task = \r
+ final ThumbnailsCacheManager.ThumbnailGenerationTask task =\r
new ThumbnailsCacheManager.ThumbnailGenerationTask(\r
fileIcon, mStorageManager, mAccount\r
- );\r
+ );\r
if (thumbnail == null) {\r
thumbnail = ThumbnailsCacheManager.mDefaultImg;\r
}\r
- final AsyncDrawable asyncDrawable = new AsyncDrawable(\r
+ final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =\r
+ new ThumbnailsCacheManager.AsyncDrawable(\r
mContext.getResources(), \r
thumbnail, \r
task\r
- );\r
+ );\r
fileIcon.setImageDrawable(asyncDrawable);\r
task.execute(file);\r
}\r
}\r
} else {\r
- fileIcon.setImageResource(\r
- DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())\r
- );\r
- }\r
-
- if (checkIfFileIsSharedWithMe(file)) {\r
- sharedWithMeIconV.setVisibility(View.VISIBLE);\r
+ fileIcon.setImageResource(DisplayUtils.getFileTypeIconId(file.getMimetype(),\r
+ file.getFileName()));\r
}\r
- } \r
- else {\r
- // TODO Re-enable when server supports folder-size calculation\r
- // if (FileStorageUtils.getDefaultSavePathFor(mAccount.name, file) != null){\r
- // fileSizeV.setVisibility(View.VISIBLE);\r
- // fileSizeV.setText(getFolderSizeHuman(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)));\r
- // } else {\r
- fileSizeV.setVisibility(View.INVISIBLE);\r
- // }\r
-
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(showRelativeTimestamp(file));\r
- checkBoxV.setVisibility(View.GONE);\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
\r
+ } else {\r
+ // Folder\r
if (checkIfFileIsSharedWithMe(file)) {\r
fileIcon.setImageResource(R.drawable.shared_with_me_folder);\r
- sharedWithMeIconV.setVisibility(View.VISIBLE);\r
+ } else if (file.isShareByLink()) {\r
+ // If folder is sharedByLink, icon folder must be changed to\r
+ // folder-public one\r
+ fileIcon.setImageResource(R.drawable.folder_public);\r
} else {\r
fileIcon.setImageResource(\r
- DisplayUtils.getResourceId(file.getMimetype(), file.getFileName())\r
+ DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName())\r
);\r
}\r
- \r
- // If folder is sharedByLink, icon folder must be changed to\r
- // folder-public one\r
- if (file.isShareByLink()) {\r
- fileIcon.setImageResource(R.drawable.folder_public);\r
- }\r
- }\r
- \r
- if (file.isShareByLink()) {\r
- sharedIconV.setVisibility(View.VISIBLE);\r
- } else {\r
- sharedIconV.setVisibility(View.GONE);\r
}\r
}\r
\r
return view;\r
}\r
-
+ \r
/**\r
* Local Folder size in human readable format\r
* \r
File dir = new File(path);\r
\r
if (dir.exists()) {\r
- long bytes = getFolderSize(dir);\r
+ long bytes = FileStorageUtils.getFolderSize(dir);\r
return DisplayUtils.bytesToHumanReadable(bytes);\r
}\r
\r
return result;\r
}\r
return 0;\r
- }
-
+ } \r
+ \r
@Override\r
public int getViewTypeCount() {\r
return 1;\r
* @param updatedStorageManager Optional updated storage manager; used to replace \r
* mStorageManager if is different (and not NULL)\r
*/\r
- public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {\r
+ public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager, boolean onlyOnDevice) {\r
mFile = directory;\r
if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
mStorageManager = updatedStorageManager;\r
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
}\r
if (mStorageManager != null) {\r
- mFiles = mStorageManager.getFolderContent(mFile, onlyOnDevice);\r
- mFiles = mStorageManager.getFolderContent(mFile);\r
++ mFiles = mStorageManager.getFolderContent(mFile, false);\r
+ mFilesOrig.clear();\r
+ mFilesOrig.addAll(mFiles);\r
+ \r
if (mJustFolders) {\r
mFiles = getFolders(mFiles);\r
}\r
mFiles = null;\r
}\r
\r
- sortDirectory();\r
- }\r
- \r
- /**\r
- * Sorts all filenames, regarding last user decision \r
- */\r
- private void sortDirectory(){\r
- switch (mSortOrder){\r
- case 0:\r
- sortByName(mSortAscending);\r
- break;\r
- case 1:\r
- sortByDate(mSortAscending);\r
- break;\r
- case 2: \r
- sortBySize(mSortAscending);\r
- break;\r
- }\r
- \r
+ mFiles = FileStorageUtils.sortFolder(mFiles);\r
notifyDataSetChanged();\r
}\r
\r
- \r
+ \r
/**\r
* Filter for getting only the folders\r
* @param files\r
&& file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));\r
}\r
\r
- /**\r
- * Sorts list by Date\r
- * @param sortAscending true: ascending, false: descending\r
- */\r
- private void sortByDate(boolean sortAscending){\r
- final Integer val;\r
- if (sortAscending){\r
- val = 1;\r
- } else {\r
- val = -1;\r
- }\r
- \r
- Collections.sort(mFiles, new Comparator<OCFile>() {\r
- public int compare(OCFile o1, OCFile o2) {\r
- if (o1.isFolder() && o2.isFolder()) {\r
- Long obj1 = o1.getModificationTimestamp();\r
- return val * obj1.compareTo(o2.getModificationTimestamp());\r
- }\r
- else if (o1.isFolder()) {\r
- return -1;\r
- } else if (o2.isFolder()) {\r
- return 1;\r
- } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){\r
- return 0;\r
- } else {\r
- Long obj1 = o1.getModificationTimestamp();\r
- return val * obj1.compareTo(o2.getModificationTimestamp());\r
- }\r
- }\r
- });\r
- }\r
- \r
- /**\r
- * Sorts list by Size\r
- * @param sortAscending true: ascending, false: descending\r
- */\r
- private void sortBySize(boolean sortAscending){\r
- final Integer val;\r
- if (sortAscending){\r
- val = 1;\r
- } else {\r
- val = -1;\r
- }\r
- \r
- Collections.sort(mFiles, new Comparator<OCFile>() {\r
- public int compare(OCFile o1, OCFile o2) {\r
- if (o1.isFolder() && o2.isFolder()) {\r
- Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));\r
- return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));\r
- }\r
- else if (o1.isFolder()) {\r
- return -1;\r
- } else if (o2.isFolder()) {\r
- return 1;\r
- } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){\r
- return 0;\r
- } else {\r
- Long obj1 = o1.getFileLength();\r
- return val * obj1.compareTo(o2.getFileLength());\r
- }\r
- }\r
- });\r
- }\r
- \r
- /**\r
- * Sorts list by Name\r
- * @param sortAscending true: ascending, false: descending\r
- */\r
- private void sortByName(boolean sortAscending){\r
- final Integer val;\r
- if (sortAscending){\r
- val = 1;\r
- } else {\r
- val = -1;\r
- }\r
- \r
- Collections.sort(mFiles, new Comparator<OCFile>() {\r
- public int compare(OCFile o1, OCFile o2) {\r
- if (o1.isFolder() && o2.isFolder()) {\r
- return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase());\r
- } else if (o1.isFolder()) {\r
- return -1;\r
- } else if (o2.isFolder()) {\r
- return 1;\r
- }\r
- return val * new AlphanumComparator().compare(o1, o2);\r
- }\r
- });\r
- }\r
- \r
public void setSortOrder(Integer order, boolean ascending) {\r
SharedPreferences.Editor editor = mAppPreferences.edit();\r
editor.putInt("sortOrder", order);\r
editor.putBoolean("sortAscending", ascending);\r
editor.commit();\r
\r
- mSortOrder = order;\r
- mSortAscending = ascending;\r
+ FileStorageUtils.mSortOrder = order;\r
+ FileStorageUtils.mSortAscending = ascending;\r
\r
- sortDirectory();\r
- } \r
+ \r
+ mFiles = FileStorageUtils.sortFolder(mFiles);\r
+ notifyDataSetChanged();\r
+ \r
+ }\r
\r
private CharSequence showRelativeTimestamp(OCFile file){\r
return DisplayUtils.getRelativeDateTimeString(mContext, file.getModificationTimestamp(),\r
DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);\r
- }
+ }\r
+ \r
+ public void setGridMode(boolean gridMode) {\r
+ mGridMode = gridMode;\r
+ }\r
}\r
- /* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog requiring confirmation before removing a given OCFile.
*
- * Triggers the removal according to the user response.
- *
- * @author David A. Velasco
+ * Triggers the removal according to the user response.
*/
import java.util.Vector;
boolean containsKeepInSync = false;
if (mTargetFile.isFolder()) {
- Vector<OCFile> files = storageManager.getFolderContent(mTargetFile);
+ Vector<OCFile> files = storageManager.getFolderContent(mTargetFile, false);
for(OCFile file: files) {
containsKeepInSync = file.keepInSync() || containsKeepInSync;
- /* ownCloud Android client application
+ /**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author masensio
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
package com.owncloud.android.ui.fragment;
import java.io.File;
- import java.util.Vector;
import android.app.Activity;
- import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.ContextMenu;
+import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.TextView;
+import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.ui.preview.PreviewMediaFragment;
+ import com.owncloud.android.utils.FileStorageUtils;
/**
* A Fragment that lists all files and folders in a given path.
*
* TODO refactorize to get rid of direct dependency on FileDisplayActivity
- *
- * @author Bartek Przybylski
- * @author masensio
- * @author David A. Velasco
*/
public class OCFileListFragment extends ExtendedListFragment {
private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
+ private final static Double THUMBNAIL_THRESHOLD = 0.5;
+
private FileFragment.ContainerActivity mContainerActivity;
private OCFile mFile = null;
private FileListListAdapter mAdapter;
- private View mFooterView;
+ private boolean mJustFolders;
private OCFile mTargetFile;
-
+
+
/**
* {@inheritDoc}
mFile = savedInstanceState.getParcelable(KEY_FILE);
}
- mFooterView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(
- R.layout.list_footer, null, false);
- setFooterView(mFooterView);
+ if (mJustFolders) {
+ setFooterEnabled(false);
+ } else {
+ setFooterEnabled(true);
+ }
Bundle args = getArguments();
- boolean justFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
+ mJustFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
mAdapter = new FileListListAdapter(
- justFolders,
- getSherlockActivity(),
+ mJustFolders,
+ getSherlockActivity(),
mContainerActivity
);
setListAdapter(mAdapter);
- registerForContextMenu(getListView());
- getListView().setOnCreateContextMenuListener(this);
- }
+ registerForContextMenu();
+ }
/**
* Saves the current listed folder.
} // exit is granted because storageManager.getFileByPath("/") never returns null
mFile = parentDir;
- listDirectory(mFile);
+ listDirectory(mFile, MainApp.getOnlyOnDevice());
onRefresh(false);
if (file != null) {
if (file.isFolder()) {
// update state and view of this fragment
- listDirectory(file);
+ listDirectory(file, MainApp.getOnlyOnDevice());
// then, notify parent activity to let it update its state and view
mContainerActivity.onBrowsedDownTo(file);
// save index and top position
/**
* Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
*/
- public void listDirectory(){
- listDirectory(null);
+ public void listDirectory(boolean onlyOnDevice){
+ listDirectory(null, onlyOnDevice);
+ }
+
+ public void refreshDirectory(){
+ listDirectory(getCurrentFile(), MainApp.getOnlyOnDevice());
}
/**
*
* @param directory File to be listed
*/
- public void listDirectory(OCFile directory) {
+ public void listDirectory(OCFile directory, boolean onlyOnDevice) {
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
if (storageManager != null) {
directory = storageManager.getFileById(directory.getParentId());
}
- mAdapter.swapDirectory(directory, storageManager);
+ mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
if (mFile == null || !mFile.equals(directory)) {
- mList.setSelectionFromTop(0, 0);
+ mCurrentListView.setSelection(0);
}
mFile = directory;
-
- // Update Footer
- TextView footerText = (TextView) mFooterView.findViewById(R.id.footerText);
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
- footerText.setText(generateFooterText(directory, onlyOnDevice));
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
+
+ updateLayout();
+
}
}
-
- private String generateFooterText(OCFile directory, boolean onlyOnDevice) {
- Integer files = 0;
- Integer folders = 0;
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
- Vector<OCFile> mFiles = storageManager.getFolderContent(mFile, onlyOnDevice);
+ private void updateLayout() {
+ if (!mJustFolders) {
+ int filesCount = 0, foldersCount = 0, imagesCount = 0;
+ int count = mAdapter.getCount();
+ OCFile file;
+ for (int i=0; i < count ; i++) {
+ file = (OCFile) mAdapter.getItem(i);
+ if (file.isFolder()) {
+ foldersCount++;
+ } else {
+ filesCount++;
+ if (file.isImage()){
+ imagesCount++;
+ }
+ }
+ }
+ // set footer text
+ setFooterText(generateFooterText(filesCount, foldersCount));
- for (OCFile ocFile : mFiles) {
- if (ocFile.isFolder()) {
- folders++;
+ // decide grid vs list view
+ if (((double)imagesCount / (double)filesCount) >= THUMBNAIL_THRESHOLD) {
+ switchToGridView();
} else {
- files++;
+ switchToListView();
}
}
+ }
+ private String generateFooterText(int filesCount, int foldersCount) {
String output = "";
-
- if (files > 0){
- if (files == 1) {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_file);
+ if (filesCount > 0){
+ if (filesCount == 1) {
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_file);
} else {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_files);
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_files);
}
}
- if (folders > 0 && files > 0){
+ if (foldersCount > 0 && filesCount > 0){
output = output + ", ";
}
- if (folders == 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folder);
- } else if (folders > 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folders);
+ if (foldersCount == 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folder);
+ } else if (foldersCount > 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folders);
}
-
+
return output;
}
-
+
+
public void sortByName(boolean descending) {
- mAdapter.setSortOrder(FileListListAdapter.SORT_NAME, descending);
+ mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
}
public void sortByDate(boolean descending) {
- mAdapter.setSortOrder(FileListListAdapter.SORT_DATE, descending);
+ mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
}
public void sortBySize(boolean descending) {
- mAdapter.setSortOrder(FileListListAdapter.SORT_SIZE, descending);
+ mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
}
-
+
+
+
}
- /* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
- import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
- import android.preference.PreferenceManager;
import android.support.v4.view.ViewPager;
import android.view.View;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import com.ortiz.touch.ExtendedViewPager;
+import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.operations.UnshareLinkOperation;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
- import com.owncloud.android.ui.activity.PinCodeActivity;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.utils.DisplayUtils;
/**
* Holds a swiping galley where image files contained in an ownCloud directory are shown
- *
- * @author David A. Velasco
*/
public class PreviewImageActivity extends FileActivity implements
FileFragment.ContainerActivity,
private View mFullScreenAnchorView;
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
actionBar.setIcon(DisplayUtils.getSeasonalIconId());
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.hide();
-
- // PIN CODE request
- if (getIntent().getExtras() != null && savedInstanceState == null && fromNotification()) {
- requestPinCode();
- }
// Make sure we're running on Honeycomb or higher to use FullScreen and
// Immersive Mode
// should not be necessary
parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
}
- mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), getStorageManager());
+ mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), getStorageManager(), MainApp.getOnlyOnDevice());
mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager);
int position = mHasSavedPosition ? mSavedPosition : mPreviewImagePagerAdapter.getFilePosition(getFile());
position = (position >= 0) ? position : 0;
@Override
protected void onResume() {
super.onResume();
- //Log_OC.e(TAG, "ACTIVITY, ONRESUME");
+
mDownloadFinishReceiver = new DownloadFinishReceiver();
IntentFilter filter = new IntentFilter(FileDownloader.getDownloadFinishMessage());
@Override
protected void onPostResume() {
- //Log_OC.e(TAG, "ACTIVITY, ONPOSTRESUME");
super.onPostResume();
}
@Override
public void onPause() {
- unregisterReceiver(mDownloadFinishReceiver);
- mDownloadFinishReceiver = null;
+ if (mDownloadFinishReceiver != null){
+ unregisterReceiver(mDownloadFinishReceiver);
+ mDownloadFinishReceiver = null;
+ }
+
super.onPause();
}
/**
- * Class waiting for broadcast events from the {@link FielDownloader} service.
+ * Class waiting for broadcast events from the {@link FileDownloader} service.
*
* Updates the UI when a download is started or finished, provided that it is relevant for the
* folder displayed in the gallery.
}
}
}
-
-
- /**
- * Launch an intent to request the PIN code to the user before letting him use the app
- */
- private void requestPinCode() {
- boolean pinStart = false;
- SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- pinStart = appPrefs.getBoolean("set_pincode", false);
- if (pinStart) {
- Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
- i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "PreviewImageActivity");
- startActivity(i);
- }
- }
@Override
public void onBrowsedDownTo(OCFile folder) {
}
return false;
}
-
}
- /* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*/
package com.owncloud.android.ui.preview;
+ import java.util.Collections;
+ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.ui.adapter.FileListListAdapter;
import com.owncloud.android.ui.fragment.FileFragment;
+ import com.owncloud.android.utils.FileStorageUtils;
/**
- * Adapter class that provides Fragment instances
- *
- * @author David A. Velasco
+ * Adapter class that provides Fragment instances
*/
//public class PreviewImagePagerAdapter extends PagerAdapter {
public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
* @param parentFolder Folder where images will be searched for.
* @param storageManager Bridge to database.
*/
- public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, FileDataStorageManager storageManager) {
+ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, FileDataStorageManager storageManager, boolean onlyOnDevice) {
super(fragmentManager);
if (fragmentManager == null) {
mAccount = account;
mStorageManager = storageManager;
- mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice);
- mImageFiles = mStorageManager.getFolderImages(parentFolder);
++ mImageFiles = mStorageManager.getFolderImages(parentFolder, false);
+
+ mImageFiles = FileStorageUtils.sortFolder(mImageFiles);
+
mObsoleteFragments = new HashSet<Object>();
mObsoletePositions = new HashSet<Integer>();
mDownloadErrors = new HashSet<Integer>();
//mFragmentManager = fragmentManager;
mCachedFragments = new HashMap<Integer, FileFragment>();
}
-
/**
* Returns the image files handled by the adapter.
OCFile file = mImageFiles.get(i);
Fragment fragment = null;
if (file.isDown()) {
- fragment = new PreviewImageFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+ fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(Integer.valueOf(i)));
} else if (mDownloadErrors.contains(Integer.valueOf(i))) {
- fragment = new FileDownloadFragment(file, mAccount, true);
+ fragment = FileDownloadFragment.newInstance(file, mAccount, true);
((FileDownloadFragment)fragment).setError(true);
mDownloadErrors.remove(Integer.valueOf(i));
} else {
- fragment = new FileDownloadFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+ fragment = FileDownloadFragment.newInstance(
+ file, mAccount, mObsoletePositions.contains(Integer.valueOf(i))
+ );
}
mObsoletePositions.remove(Integer.valueOf(i));
return fragment;
- /* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import android.graphics.Matrix;
import android.graphics.BitmapFactory.Options;
import android.media.ExifInterface;
+ import android.net.Uri;
+ import android.webkit.MimeTypeMap;
+
+ import java.io.File;
/**
* Utility class with methods for decoding Bitmaps.
- *
- * @author David A. Velasco
*/
public class BitmapUtils {
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
-
+
+ // calculates the largest inSampleSize value (for smallest sample) that is a power of 2 and keeps both
+ // height and width **larger** than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
/**
+ * Converts an HSL color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ * Assumes h, s, and l are contained in the set [0, 1] and
+ * returns r, g, and b in the set [0, 255].
+ * from: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+ *
+ * @param integer h The hue
+ * @param Integer s The saturation
+ * @param Integer l The lightness
+ * @return Array The RGB representation
+ */
+ public static int[] hslToRgb(Double h, Double s, Double l){
+ Double r, g, b;
+
+ if(s == 0){
+ r = g = b = l; // achromatic
+ } else {
+ Double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ Double p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3) * 255;
+ g = hue2rgb(p, q, h) * 255;
+ b = hue2rgb(p, q, h - 1/3) * 255;
+ }
+
+
+ int[] array = {r.intValue(), g.intValue(), b.intValue()};
+ return array;
+ }
+
+ private static Double hue2rgb(Double p, Double q, Double t){
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ }
+
++
++ /**
+ * Checks if file passed is an image
+ * @param file
+ * @return true/false
+ */
+ public static boolean isImage(File file) {
+ Uri selectedUri = Uri.fromFile(file);
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString().toLowerCase());
+ String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
+
+ return (mimeType != null && mimeType.startsWith("image/"));
+ }
}