Merge pull request #1493 from owncloud/reliable_uploads_actions_uploads_view

Uploads view added to monitor and handle uploads progress
This commit is contained in:
David A. Velasco 2016-03-30 16:16:22 +02:00
commit b5a9594023
84 changed files with 5913 additions and 2146 deletions

View file

@ -3,7 +3,7 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

1
.gitignore vendored
View file

@ -21,6 +21,7 @@ oc_workaround/local.properties
oc_framework/local.properties
oc_framework-test-project/local.properties
tests/local.properties
lint.xml
# Mac .DS_Store files
.DS_Store

View file

@ -163,12 +163,23 @@
<service android:name=".media.MediaService" />
<activity android:name=".ui.activity.PassCodeActivity" />
<activity android:name=".ui.activity.ConflictsResolveActivity" />
<activity android:name=".ui.activity.GenericExplanationActivity" />
<activity android:name=".ui.activity.ErrorsWhileCopyingHandlerActivity" />
<activity android:name=".ui.activity.LogHistoryActivity" />
<activity android:name=".ui.activity.ConflictsResolveActivity"/>
<activity android:name=".ui.activity.GenericExplanationActivity"/>
<activity android:name=".ui.activity.ErrorsWhileCopyingHandlerActivity"/>
<activity android:name=".ui.activity.LogHistoryActivity"/>
<receiver android:name=".files.InstantUploadBroadcastReceiver" >
<activity android:name=".ui.errorhandling.ErrorShowActivity" />
<activity android:name=".ui.activity.UploadListActivity" />
<receiver android:name=".files.services.ConnectivityActionReceiver"
android:enabled="true" android:label="ConnectivityActionReceiver">
<intent-filter>
<!--action android:name="android.net.conn.CONNECTIVITY_CHANGE"/-->
<action android:name="android.net.wifi.STATE_CHANGE"/>
</intent-filter>
</receiver>
<receiver android:name=".files.InstantUploadBroadcastReceiver">
<intent-filter>
<!-- unofficially supported by many Android phones but not by HTC devices: -->
@ -183,9 +194,6 @@
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<receiver android:name=".files.BootupBroadcastReceiver" >
<intent-filter>

@ -1 +1 @@
Subproject commit 573afa15382b67cc84f67fc9a7b2329a72ecb352
Subproject commit 39e3ddaa07b0943b034b34a84a33b4dc4c7475d0

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed state of button, only change: color of actual button -->
<item android:state_pressed="true" >
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- shadow, a little down and a little to the right -->
<item><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#99000000" android:startColor="#99000000" />
</shape></item>
<!-- this is the actual button -->
<item android:bottom="1px" android:right="1px"><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#47ffffff" android:startColor="#97ffffff" />
</shape></item>
</layer-list>
</item>
<!-- focused state of button, only change: color of actual button -->
<item android:state_focused="true" >
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- shadow, a little down and a little to the right -->
<item><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#22000000" android:startColor="#22000000" />
</shape></item>
<!-- this is the actual button -->
<item android:bottom="1px" android:right="1px"><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#47ffffff" android:startColor="#97ffffff" />
</shape></item>
</layer-list>
</item> <!-- normal state of button. Template for other states. -->
<item>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- shadow, a little down and a little to the right -->
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#55000000" android:startColor="#55000000" />
</shape></item>
<!-- this is the actual button -->
<item android:bottom="1px" android:right="1px">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<gradient android:angle="270" android:endColor="#47ffffff" android:startColor="#97ffffff" />
</shape>
</item>
</layer-list>
</item>
</selector>

BIN
res/drawable/upload_failed.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

BIN
res/drawable/upload_finished.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

View file

@ -0,0 +1,11 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/errorTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This must be a clone of list_fragment.xml
EXCEPT: ExpandableListView must be used for @+id/swipe_refresh_files_emptyView
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_files"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ExpandableListView
android:id="@+id/list_root"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_files_emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/empty_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="@string/empty"
android:layout_gravity="center"
android:visibility="visible" />
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

View file

@ -0,0 +1,19 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp" >
<TextView
android:id="@+id/uploadListGroupName"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:divider="@null"
android:dividerHeight="0dp"
android:ellipsize="middle"
android:showDividers="none"
android:textColor="@color/color_accent"
android:paddingLeft="16dp"
/>
</RelativeLayout>

127
res/layout/upload_list_item.xml Executable file
View file

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ListItemLayout"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content"
android:paddingTop="@dimen/standard_half_padding"
android:paddingBottom="@dimen/standard_half_padding"
>
<FrameLayout
android:layout_width="60dp"
android:layout_height="72dp"
android:focusable="false"
android:focusableInTouchMode="false">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/file_icon_size"
android:layout_height="@dimen/file_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_menu_archive" />
</FrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/upload_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="middle"
android:singleLine="true"
android:textColor="@color/textColor"
android:text="@string/placeholder_filename"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/upload_file_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:ellipsize="middle"
android:singleLine="true"
android:text="@string/placeholder_filesize"
android:textSize="12sp"/>
<TextView
android:id="@+id/upload_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:text="@string/placeholder_timestamp"
android:textSize="12sp"/>
<TextView
android:id="@+id/upload_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:text="@string/uploads_view_upload_status_succeeded"
android:textSize="12sp"/>
</LinearLayout>
<ProgressBar
android:id="@+id/upload_progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
<TextView
android:id="@+id/upload_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:singleLine="true"
android:text="@string/auth_username"
android:textSize="12dip"/>
<TextView
android:id="@+id/upload_remote_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:ellipsize="middle"
android:singleLine="true"
android:text="@string/instant_upload_path"
android:textSize="12dip"/>
</LinearLayout>
<FrameLayout
android:layout_width="56dp"
android:layout_height="72dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_gravity="center_vertical"
android:paddingLeft="8dp"
>
<ImageButton
android:id="@+id/upload_right_button"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center"
android:background="@android:color/transparent"
/>
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,24 @@
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_color">
<FrameLayout
android:id="@+id/upload_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<include
layout="@layout/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>

34
res/menu/upload_list_menu.xml Executable file
View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
Copyright (C) 2012-2013 ownCloud Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2,
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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_retry_uploads"
android:title="@string/action_retry_uploads"/>
<item
android:id="@+id/action_clear_failed_uploads"
android:title="@string/action_clear_failed_uploads"/>
<item
android:id="@+id/action_clear_successfull_uploads"
android:title="@string/action_clear_successful_uploads"/>
<item
android:id="@+id/action_clear_finished_uploads"
android:title="@string/action_clear_finished_uploads"/>
</menu>

View file

@ -24,6 +24,7 @@
<!--<item>@string/prefs_accounts</item>-->
<item>@string/drawer_item_all_files</item>
<!--<item>@string/drawer_item_on_device</item>-->
<item>@string/drawer_item_uploads_list</item>
<item>@string/actionbar_settings</item>
<item>@string/actionbar_logger</item>
</string-array>
@ -34,6 +35,7 @@
<!--<item>@string/drawer_item_accounts</item>-->
<item>@string/drawer_item_all_files</item>
<!--<item>@string/drawer_item_on_device</item>-->
<item>@string/drawer_item_uploads_list</item>
<item>@string/drawer_item_settings</item>
<item>@string/drawer_item_logs</item>
</string-array>

View file

@ -26,6 +26,7 @@
<!-- TODO re-enable when "On Device" is available
<string name="drawer_item_on_device">On device</string>-->
<string name="drawer_item_settings">Settings</string>
<string name="drawer_item_uploads_list">Uploads</string>
<string name="drawer_item_logs">Logs</string>
<string name="drawer_close">Close</string>
<string name="drawer_open">Open</string>
@ -76,6 +77,11 @@
<string name="file_list_loading">Loading&#8230;</string>
<string name="file_list_no_app_for_file_type">No app found for file type!</string>
<string name="local_file_list_empty">There are no files in this folder.</string>
<string name="upload_list_empty">No uploads available.</string>
<string name="file_list_folder">folder</string>
<string name="file_list_folders">folders</string>
<string name="file_list_file">file</string>
<string name="file_list_files">files</string>
<string name="filedetails_select_file">Tap on a file to display additional information.</string>
<string name="filedetails_size">Size:</string>
<string name="filedetails_type">Type:</string>
@ -89,6 +95,8 @@
<string name="common_yes">Yes</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
<string name="common_remove_upload">Remove upload</string>
<string name="common_retry_upload">Retry upload</string>
<string name="common_cancel_sync">Cancel sync</string>
<string name="common_cancel">Cancel</string>
<string name="common_save_exit">Save &amp; exit</string>
@ -108,6 +116,25 @@
<string name="uploader_upload_failed_ticker">Upload failed</string>
<string name="uploader_upload_failed_content_single">Upload of %1$s could not be completed</string>
<string name="uploader_upload_failed_credentials_error">Upload failed, you need to log in again</string>
<string name="uploads_view_title">Uploads</string>
<string name="uploads_view_group_current_uploads">Current</string>
<string name="uploads_view_group_failed_uploads">Failed (tap to retry)</string>
<string name="uploads_view_group_finished_uploads">Uploaded</string>
<string name="uploads_view_upload_status_succeeded">Completed</string>
<string name="uploads_view_upload_status_cancelled">Cancelled</string>
<string name="uploads_view_upload_status_paused">Paused</string>
<string name="uploads_view_upload_status_failed_connection_error">Connection error</string>
<string name="uploads_view_upload_status_failed_retry">Upload will be retried shortly</string>
<string name="uploads_view_upload_status_failed_credentials_error">Credentials error</string>
<string name="uploads_view_upload_status_failed_folder_error">Folder error</string>
<string name="uploads_view_upload_status_failed_file_error">File error</string>
<string name="uploads_view_upload_status_failed_localfile_error">Local file not found</string>
<string name="uploads_view_upload_status_failed_permission_error">Permission error</string>
<string name="uploads_view_upload_status_conflict">Conflict</string>
<string name="uploads_view_upload_status_service_interrupted">App was terminated</string>
<string name="uploads_view_upload_status_unknown_fail">Unknown error</string>
<string name="uploads_view_upload_status_waiting_for_wifi">Waiting for wifi connectivity</string>
<string name="uploads_view_later_waiting_to_upload">Waiting to upload</string>
<string name="downloader_download_in_progress_ticker">Downloading &#8230;</string>
<string name="downloader_download_in_progress_content">%1$d%% Downloading %2$s</string>
<string name="downloader_download_succeeded_ticker">Download succeeded</string>
@ -219,6 +246,7 @@
<string name="filename_forbidden_charaters_from_server">File name contains at least one invalid character</string>
<string name="filename_empty">File name cannot be empty</string>
<string name="wait_a_moment">Wait a moment</string>
<string name="wait_checking_credentials">Checking stored credentials</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 &#8230;</string>
@ -405,6 +433,11 @@
<string name="edit_share_unshare">Stop sharing</string>
<string name="edit_share_done">done</string>
<string name="action_retry_uploads">Retry failed</string>
<string name="action_clear_failed_uploads">Clear failed</string>
<string name="action_clear_successful_uploads">Clear successful</string>
<string name="action_clear_finished_uploads">Clear all finished</string>
<string name="action_switch_grid_view">Grid view</string>
<string name="action_switch_list_view">List view</string>
@ -414,5 +447,6 @@
<string name="manage_space_error">Some files could not be deleted.</string>
<string name="permission_storage_access">Additional permissions required to upload &amp; download files.</string>
<string name="local_file_not_found_toast">The file was not found locally</string>
</resources>

View file

@ -29,6 +29,7 @@
<item name="colorAccent">@color/color_accent</item>
<item name="android:alertDialogTheme">@style/Theme.ownCloud.Dialog</item>
<item name="alertDialogTheme">@style/ownCloud.AlertDialog</item>
<item name="android:windowBackground">@color/background_color</item>
<item name="searchViewStyle">@style/ownCloud.SearchView</item>
</style>
@ -41,6 +42,7 @@
<item name="colorAccent">@color/color_accent</item>
<item name="android:alertDialogTheme">@style/Theme.ownCloud.Dialog</item>
<item name="alertDialogTheme">@style/ownCloud.AlertDialog</item>
<item name="android:windowBackground">@color/background_color</item>
<item name="searchViewStyle">@style/ownCloud.SearchView</item>
</style>

View file

@ -166,7 +166,9 @@ public class MainApp extends Application {
return getAppContext().getResources().getString(R.string.db_name);
}
// data_folder
/**
* name of data_folder, e.g., "owncloud"
*/
public static String getDataFolder() {
return getAppContext().getResources().getString(R.string.data_folder);
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -20,7 +20,12 @@
package com.owncloud.android.authentication;
import java.util.Locale;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import com.owncloud.android.MainApp;
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
@ -28,12 +33,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import java.util.Locale;
public class AccountUtils {
@ -108,6 +108,22 @@ public class AccountUtils {
return false;
}
/**
* Returns owncloud account identified by accountName or null if it does not exist.
* @param context
* @param accountName name of account to be returned
* @return owncloud account named accountName
*/
public static Account getOwnCloudAccountByName(Context context, String accountName) {
Account[] ocAccounts = AccountManager.get(context).getAccountsByType(
MainApp.getAccountType());
for (Account account : ocAccounts) {
if(account.name.equals(accountName))
return account;
}
return null;
}
public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {
boolean result = false;

View file

@ -65,6 +65,8 @@ import android.widget.Toast;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudCredentials;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
@ -925,6 +927,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} else {
checkBasicAuthorization();
}
}
@ -1465,6 +1468,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
* the new credentials when needed.
*/
private void updateAccountAuthentication() throws AccountNotFoundException {
Bundle response = new Bundle();
response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
@ -1489,7 +1494,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
}
// remove managed clients for this account to enforce creation with fresh credentials
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, this);
OwnCloudClientManagerFactory.getDefaultSingleton().removeClientFor(ocAccount);
setAccountAuthenticatorResult(response);
final Intent intent = new Intent();
intent.putExtras(response);
setResult(RESULT_OK, intent);
}

View file

@ -254,6 +254,20 @@ public class FileDataStorageManager {
}
public void saveNewFile(OCFile newFile) {
String remoteParentPath = new File(newFile.getRemotePath()).getParent();
remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
OCFile parent = getFileByPath(remoteParentPath);
if (parent != null) {
newFile.setParentId(parent.getFileId());
saveFile(newFile);
} else {
throw new IllegalArgumentException("Saving a new file in an unexisting folder");
}
}
/**
* Inserts or updates the list of files contained in a given folder.
* <p/>
@ -2043,4 +2057,5 @@ public class FileDataStorageManager {
}
return capability;
}
}

View file

@ -4,7 +4,7 @@
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -22,6 +22,9 @@
package com.owncloud.android.datamodel;
import java.io.File;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Parcel;
@ -30,9 +33,8 @@ import android.webkit.MimeTypeMap;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import third_parties.daveKoeller.AlphanumComparator;
public class OCFile implements Parcelable, Comparable<OCFile> {
public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {

View file

@ -0,0 +1,514 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* @author David A. Velasco
* @author masensio
* Copyright (C) 2016 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.datamodel;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.UploadFileOperation;
import java.util.Calendar;
import java.util.Observable;
/**
* Database helper for storing list of files to be uploaded, including status
* information for each file.
*/
public class UploadsStorageManager extends Observable {
private ContentResolver mContentResolver;
static private final String TAG = UploadsStorageManager.class.getSimpleName();
public enum UploadStatus {
/**
* Upload currently in progress or scheduled to be executed.
*/
UPLOAD_IN_PROGRESS(0),
/**
* Last upload failed.
*/
UPLOAD_FAILED(1),
/**
* Upload was successful.
*/
UPLOAD_SUCCEEDED(2);
private final int value;
UploadStatus(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static UploadStatus fromValue(int value) {
switch (value) {
case 0:
return UPLOAD_IN_PROGRESS;
case 1:
return UPLOAD_FAILED;
case 2:
return UPLOAD_SUCCEEDED;
}
return null;
}
}
public UploadsStorageManager(ContentResolver contentResolver) {
if (contentResolver == null) {
throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
}
mContentResolver = contentResolver;
}
/**
* Stores an upload object in DB.
*
* @param ocUpload Upload object to store
* @return upload id, -1 if the insert process fails.
*/
public long storeUpload(OCUpload ocUpload) {
Log_OC.v(TAG, "Inserting " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus());
ContentValues cv = new ContentValues();
cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath());
cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath());
cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName());
cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize());
cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
cv.put(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR, ocUpload.getLocalAction());
cv.put(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE, ocUpload.isForceOverwrite() ? 1 : 0);
cv.put(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER, ocUpload.isCreateRemoteFolder() ? 1 : 0);
cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());
cv.put(ProviderTableMeta.UPLOADS_CREATED_BY, ocUpload.getCreadtedBy());
Uri result = getDB().insert(ProviderTableMeta.CONTENT_URI_UPLOADS, cv);
Log_OC.d(TAG, "storeUpload returns with: " + result + " for file: " + ocUpload.getLocalPath());
if (result == null) {
Log_OC.e(TAG, "Failed to insert item " + ocUpload.getLocalPath() + " into upload db.");
return -1;
} else {
long new_id = Long.parseLong(result.getPathSegments().get(1));
ocUpload.setUploadId(new_id);
notifyObserversNow();
return new_id;
}
}
/**
* Update an upload object in DB.
*
* @param ocUpload Upload object with state to update
* @return num of updated uploads.
*/
public int updateUpload(OCUpload ocUpload) {
Log_OC.v(TAG, "Updating " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus());
ContentValues cv = new ContentValues();
cv.put(ProviderTableMeta.UPLOADS_LOCAL_PATH, ocUpload.getLocalPath());
cv.put(ProviderTableMeta.UPLOADS_REMOTE_PATH, ocUpload.getRemotePath());
cv.put(ProviderTableMeta.UPLOADS_ACCOUNT_NAME, ocUpload.getAccountName());
cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());
cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, ocUpload.getUploadEndTimestamp());
int result = getDB().update(ProviderTableMeta.CONTENT_URI_UPLOADS,
cv,
ProviderTableMeta._ID + "=?",
new String[]{String.valueOf(ocUpload.getUploadId())}
);
Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath());
if (result != 1) {
Log_OC.e(TAG, "Failed to update item " + ocUpload.getLocalPath() + " into upload db.");
} else {
notifyObserversNow();
}
return result;
}
private int updateUploadInternal(Cursor c, UploadStatus status, UploadResult result, String remotePath,
String localPath) {
int r = 0;
while (c.moveToNext()) {
// read upload object and update
OCUpload upload = createOCUploadFromCursor(c);
String path = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_PATH));
Log_OC.v(
TAG,
"Updating " + path + " with status:" + status + " and result:"
+ (result == null ? "null" : result.toString()) + " (old:"
+ upload.toFormattedString() + ")");
upload.setUploadStatus(status);
upload.setLastResult(result);
upload.setRemotePath(remotePath);
if(localPath != null) {
upload.setLocalPath(localPath);
}
if (status == UploadStatus.UPLOAD_SUCCEEDED) {
upload.setUploadEndTimestamp(Calendar.getInstance().getTimeInMillis());
}
// store update upload object to db
r = updateUpload(upload);
}
return r;
}
/**
* Update upload status of file uniquely referenced by id.
*
* @param id upload id.
* @param status new status.
* @param result new result of upload operation
* @param remotePath path of the file to upload in the ownCloud storage
* @param localPath path of the file to upload in the device storage
* @return 1 if file status was updated, else 0.
*/
public int updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath,
String localPath) {
//Log_OC.v(TAG, "Updating "+filepath+" with uploadStatus="+status +" and result="+result);
int returnValue = 0;
Cursor c = getDB().query(
ProviderTableMeta.CONTENT_URI_UPLOADS,
null,
ProviderTableMeta._ID + "=?",
new String[]{String.valueOf(id)},
null
);
if (c.getCount() != 1) {
Log_OC.e(TAG, c.getCount() + " items for id=" + id
+ " available in UploadDb. Expected 1. Failed to update upload db.");
} else {
returnValue = updateUploadInternal(c, status, result, remotePath, localPath);
}
c.close();
return returnValue;
}
/**
* Should be called when some value of this DB was changed. All observers
* are informed.
*/
public void notifyObserversNow() {
Log_OC.d(TAG, "notifyObserversNow");
setChanged();
notifyObservers();
}
/**
* Remove an upload from the uploads list, known its target account and remote path.
*
* @param upload Upload instance to remove from persisted storage.
*
* @return true when the upload was stored and could be removed.
*/
public int removeUpload(OCUpload upload) {
int result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta._ID + "=?" ,
new String[]{Long.toString(upload.getUploadId())}
);
Log_OC.d(TAG, "delete returns " + result + " for upload " + upload);
if (result > 0) {
notifyObserversNow();
}
return result;
}
/**
* Remove an upload from the uploads list, known its target account and remote path.
*
* @param accountName Name of the OC account target of the upload to remove.
* @param remotePath Absolute path in the OC account target of the upload to remove.
* @return true when one or more upload entries were removed
*/
public int removeUpload(String accountName, String remotePath) {
int result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?" ,
new String[]{accountName, remotePath}
);
Log_OC.d(TAG, "delete returns " + result + " for file " + remotePath + " in " + accountName);
if (result > 0) {
notifyObserversNow();
}
return result;
}
/**
* Remove all the uploads of a given account from the uploads list.
*
* @param accountName Name of the OC account target of the uploads to remove.
* @return true when one or more upload entries were removed
*/
public int removeUploads(String accountName) {
int result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?",
new String[]{accountName}
);
Log_OC.d(TAG, "delete returns " + result + " for uploads in " + accountName);
if (result > 0) {
notifyObserversNow();
}
return result;
}
public OCUpload[] getAllStoredUploads() {
return getUploads(null, null);
}
private OCUpload[] getUploads(String selection, String[] selectionArgs) {
Cursor c = getDB().query(
ProviderTableMeta.CONTENT_URI_UPLOADS,
null,
selection,
selectionArgs,
null
);
OCUpload[] list = new OCUpload[c.getCount()];
if (c.moveToFirst()) {
do {
OCUpload upload = createOCUploadFromCursor(c);
if (upload == null) {
Log_OC.e(TAG, "OCUpload could not be created from cursor");
} else {
list[c.getPosition()] = upload;
}
} while (c.moveToNext());
}
c.close();
return list;
}
private OCUpload createOCUploadFromCursor(Cursor c) {
OCUpload upload = null;
if (c != null) {
String localPath = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_PATH));
String remotePath = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_REMOTE_PATH));
String accountName = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_ACCOUNT_NAME));
upload = new OCUpload(localPath, remotePath, accountName);
upload.setFileSize(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_FILE_SIZE)));
upload.setUploadId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
upload.setUploadStatus(
UploadStatus.fromValue(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_STATUS)))
);
upload.setLocalAction(c.getInt(c.getColumnIndex((ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR))));
upload.setForceOverwrite(c.getInt(
c.getColumnIndex(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE)) == 1);
upload.setCreateRemoteFolder(c.getInt(
c.getColumnIndex(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1);
upload.setUploadEndTimestamp(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP)));
upload.setLastResult(UploadResult.fromValue(
c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_LAST_RESULT))));
upload.setCreatedBy(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_CREATED_BY)));
}
return upload;
}
/**
* Get all uploads which are currently being uploaded or waiting in the queue to be uploaded.
*/
public OCUpload[] getCurrentAndPendingUploads() {
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value, null);
}
/**
* Get all unrecoverably failed. Upload of these should/must/will not be
* retried.
*/
public OCUpload[] getFailedUploads() {
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null);
}
/**
* Get all uploads which where successfully completed.
*/
public OCUpload[] getFinishedUploads() {
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, null);
}
private ContentResolver getDB() {
return mContentResolver;
}
public long clearFailedUploads() {
long result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null
);
Log_OC.d(TAG, "delete all failed uploads");
if (result > 0) {
notifyObserversNow();
}
return result;
}
public long clearSuccessfulUploads() {
long result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta.UPLOADS_STATUS + "=="+ UploadStatus.UPLOAD_SUCCEEDED.value, null
);
Log_OC.d(TAG, "delete all successful uploads");
if (result > 0) {
notifyObserversNow();
}
return result;
}
public long clearAllFinishedUploads() {
String[] whereArgs = new String[2];
whereArgs[0] = String.valueOf(UploadStatus.UPLOAD_SUCCEEDED.value);
whereArgs[1] = String.valueOf(UploadStatus.UPLOAD_FAILED.value);
long result = getDB().delete(
ProviderTableMeta.CONTENT_URI_UPLOADS,
ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=?",
whereArgs
);
Log_OC.d(TAG, "delete all finished uploads");
if (result > 0) {
notifyObserversNow();
}
return result;
}
/**
* Updates the persistent upload database with upload result.
*/
public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
// result: success or fail notification
Log_OC.d(TAG, "updateDataseUploadResult uploadResult: " + uploadResult + " upload: " + upload);
if (uploadResult.isCancelled()) {
removeUpload(
upload.getAccount().name,
upload.getRemotePath()
);
} else {
String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
? upload.getStoragePath() : null;
if (uploadResult.isSuccess()) {
updateUploadStatus(
upload.getOCUploadId(),
UploadStatus.UPLOAD_SUCCEEDED,
UploadResult.UPLOADED,
upload.getRemotePath(),
localPath
);
} else {
updateUploadStatus(
upload.getOCUploadId(),
UploadStatus.UPLOAD_FAILED,
UploadResult.fromOperationResult(uploadResult),
upload.getRemotePath(),
localPath
);
}
}
}
/**
* Updates the persistent upload database with an upload now in progress.
*/
public void updateDatabaseUploadStart(UploadFileOperation upload) {
String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
? upload.getStoragePath() : null;
updateUploadStatus(
upload.getOCUploadId(),
UploadStatus.UPLOAD_IN_PROGRESS,
UploadResult.UNKNOWN,
upload.getRemotePath(),
localPath
);
}
/**
* Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS
* to UploadStatus.UPLOAD_FAILED
*
* @return Number of uploads which status was changed.
*/
public int failInProgressUploads(UploadResult fail) {
Log_OC.v(TAG, "Updating state of any killed upload");
ContentValues cv = new ContentValues();
cv.put(ProviderTableMeta.UPLOADS_STATUS, UploadStatus.UPLOAD_FAILED.getValue());
cv.put(
ProviderTableMeta.UPLOADS_LAST_RESULT,
fail != null ? fail.getValue() : UploadResult.UNKNOWN.getValue()
);
cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, Calendar.getInstance().getTimeInMillis());
int result = getDB().update(
ProviderTableMeta.CONTENT_URI_UPLOADS,
cv,
ProviderTableMeta.UPLOADS_STATUS + "=?",
new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())}
);
if (result == 0) {
Log_OC.v(TAG, "No upload was killed");
} else {
Log_OC.w(TAG, Integer.toString(result) + " uploads where abruptly interrupted");
notifyObserversNow();
}
return result;
}
}

View file

@ -1,127 +0,0 @@
/**
* ownCloud Android client application
*
* @author Bartek Przybylski
* Copyright (C) 2011-2012 Bartek Przybylski
* 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.db;
import com.owncloud.android.MainApp;
import com.owncloud.android.lib.common.utils.Log_OC;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* Custom database helper for ownCloud
*/
public class DbHandler {
private SQLiteDatabase mDB;
private OpenerHelper mHelper;
private final String mDatabaseName;
private final int mDatabaseVersion = 3;
private final String TABLE_INSTANT_UPLOAD = "instant_upload";
public static final int UPLOAD_STATUS_UPLOAD_LATER = 0;
public static final int UPLOAD_STATUS_UPLOAD_FAILED = 1;
public DbHandler(Context context) {
mDatabaseName = MainApp.getDBName();
mHelper = new OpenerHelper(context);
mDB = mHelper.getWritableDatabase();
}
public void close() {
mDB.close();
}
public boolean putFileForLater(String filepath, String account, String message) {
ContentValues cv = new ContentValues();
cv.put("path", filepath);
cv.put("account", account);
cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER);
cv.put("message", message);
long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv);
Log_OC.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath);
return result != -1;
}
public int updateFileState(String filepath, Integer status, String message) {
ContentValues cv = new ContentValues();
cv.put("attempt", status);
cv.put("message", message);
int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath });
Log_OC.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath);
return result;
}
public Cursor getAwaitingFiles() {
return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt=" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
}
public Cursor getFailedFiles() {
return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt>" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
}
public void clearFiles() {
mDB.delete(TABLE_INSTANT_UPLOAD, null, null);
}
/**
*
* @param localPath
* @return true when one or more pending files was removed
*/
public boolean removeIUPendingFile(String localPath) {
long result = mDB.delete(TABLE_INSTANT_UPLOAD, "path = ?", new String[] { localPath });
Log_OC.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath);
return result != 0;
}
private class OpenerHelper extends SQLiteOpenHelper {
public OpenerHelper(Context context) {
super(context, mDatabaseName, null, mDatabaseVersion);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT,"
+ " account TEXT,attempt INTEGER,message TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;");
}
db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;");
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//downgrading is the exception, so deleting and re-creating is acceptable.
//otherwise exception will be thrown (cannot downgrade) and oc app will crash.
db.execSQL("DROP TABLE IF EXISTS " + TABLE_INSTANT_UPLOAD + ";");
onCreate(db);
}
}
}

View file

@ -0,0 +1,427 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* @author masensio
* @author David A. Velasco
* Copyright (C) 2016 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.db;
import android.accounts.Account;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.utils.MimetypeIconUtil;
import java.io.File;
/**
* Stores all information in order to start upload operations. PersistentUploadObject can
* be stored persistently by {@link UploadsStorageManager}.
*
*/
public class OCUpload implements Parcelable {
private static final String TAG = OCUpload.class.getSimpleName();
private long mId;
/**
* Absolute path in the local file system to the file to be uploaded
*/
private String mLocalPath;
/**
* Absolute path in the remote account to set to the uploaded file (not for its parent folder!)
*/
private String mRemotePath;
/**
* Name of Owncloud account to upload file to.
*/
private String mAccountName;
/**
* File size
*/
private long mFileSize;
/**
* Local action for upload. (0 - COPY, 1 - MOVE, 2 - FORGET)
*/
private int mLocalAction;
/**
* Overwrite destination file?
*/
private boolean mForceOverwrite;
/**
* Create destination folder?
*/
private boolean mIsCreateRemoteFolder;
/**
* Status of upload (later, in_progress, ...).
*/
private UploadStatus mUploadStatus;
/**
* Result from last upload operation. Can be null.
*/
private UploadResult mLastResult;
/**
* Defines the origin of the upload; see constants CREATED_ in {@link UploadFileOperation}
*/
private int mCreatedBy;
/*
* When the upload ended
*/
private long mUploadEndTimeStamp;
/**
* Main constructor
*
* @param localPath Absolute path in the local file system to the file to be uploaded.
* @param remotePath Absolute path in the remote account to set to the uploaded file.
* @param accountName Name of an ownCloud account to update the file to.
*/
public OCUpload(String localPath, String remotePath, String accountName) {
if (localPath == null || !localPath.startsWith(File.separator)) {
throw new IllegalArgumentException("Local path must be an absolute path in the local file system");
}
if (remotePath == null || !remotePath.startsWith(OCFile.PATH_SEPARATOR)) {
throw new IllegalArgumentException("Remote path must be an absolute path in the local file system");
}
if (accountName == null || accountName.length() < 1) {
throw new IllegalArgumentException("Invalid account name");
}
resetData();
mLocalPath = localPath;
mRemotePath = remotePath;
mAccountName = accountName;
}
/**
* Convenience constructor to reupload already existing {@link OCFile}s
*
* @param ocFile {@link OCFile} instance to update in the remote server.
* @param account ownCloud {@link Account} where ocFile is contained.
*/
public OCUpload(OCFile ocFile, Account account) {
this(ocFile.getStoragePath(), ocFile.getRemotePath(), account.name);
}
/**
* Reset all the fields to default values.
*/
private void resetData() {
mRemotePath = "";
mLocalPath = "";
mAccountName = "";
mFileSize = -1;
mId = -1;
mLocalAction = FileUploader.LOCAL_BEHAVIOUR_COPY;
mForceOverwrite = false;
mIsCreateRemoteFolder = false;
mUploadStatus = UploadStatus.UPLOAD_IN_PROGRESS;
mLastResult = UploadResult.UNKNOWN;
mCreatedBy = UploadFileOperation.CREATED_BY_USER;
}
// Getters & Setters
public void setUploadId(long id) {
mId = id;
}
public long getUploadId() {
return mId;
}
/**
* @return the uploadStatus
*/
public UploadStatus getUploadStatus() {
return mUploadStatus;
}
/**
* Sets uploadStatus AND SETS lastResult = null;
* @param uploadStatus the uploadStatus to set
*/
public void setUploadStatus(UploadStatus uploadStatus) {
this.mUploadStatus = uploadStatus;
setLastResult(UploadResult.UNKNOWN);
}
/**
* @return the lastResult
*/
public UploadResult getLastResult() {
return mLastResult;
}
/**
* @param lastResult the lastResult to set
*/
public void setLastResult(UploadResult lastResult) {
this.mLastResult = ((lastResult != null) ? lastResult : UploadResult.UNKNOWN);
}
/**
* @return the localPath
*/
public String getLocalPath() {
return mLocalPath;
}
public void setLocalPath(String localPath) {
mLocalPath = localPath;
}
/**
* @return the remotePath
*/
public String getRemotePath() {
return mRemotePath;
}
/**
* @param remotePath
*/
public void setRemotePath(String remotePath) {
mRemotePath = remotePath;
}
/**
* @return File size
*/
public long getFileSize() {
return mFileSize;
}
public void setFileSize(long fileSize) {
mFileSize = fileSize;
}
/**
* @return the mimeType
*/
public String getMimeType() {
return MimetypeIconUtil.getBestMimeTypeByFilename(mLocalPath);
}
/**
* @return the localAction
*/
public int getLocalAction() {
return mLocalAction;
}
/**
* @param localAction the localAction to set
*/
public void setLocalAction(int localAction) {
this.mLocalAction = localAction;
}
/**
* @return the forceOverwrite
*/
public boolean isForceOverwrite() {
return mForceOverwrite;
}
/**
* @param forceOverwrite the forceOverwrite to set
*/
public void setForceOverwrite(boolean forceOverwrite) {
this.mForceOverwrite = forceOverwrite;
}
/**
* @return the isCreateRemoteFolder
*/
public boolean isCreateRemoteFolder() {
return mIsCreateRemoteFolder;
}
/**
* @param isCreateRemoteFolder the isCreateRemoteFolder to set
*/
public void setCreateRemoteFolder(boolean isCreateRemoteFolder) {
this.mIsCreateRemoteFolder = isCreateRemoteFolder;
}
/**
* @return the accountName
*/
public String getAccountName() {
return mAccountName;
}
/**
* Returns owncloud account as {@link Account} object.
*/
public Account getAccount(Context context) {
return AccountUtils.getOwnCloudAccountByName(context, getAccountName());
}
public void setCreatedBy(int createdBy) {
mCreatedBy = createdBy;
}
public int getCreadtedBy() {
return mCreatedBy;
}
public void setUploadEndTimestamp(long uploadEndTimestamp) {
mUploadEndTimeStamp = uploadEndTimestamp;
}
public long getUploadEndTimestamp(){
return mUploadEndTimeStamp;
}
/**
* For debugging purposes only.
*/
public String toFormattedString() {
try {
String localPath = getLocalPath() != null ? getLocalPath() : "";
return localPath + " status:" + getUploadStatus() + " result:" +
(getLastResult() == null ? "null" : getLastResult().getValue());
} catch (NullPointerException e){
Log_OC.d(TAG, "Exception " + e.toString() );
return (e.toString());
}
}
/****
*
*/
public static final Parcelable.Creator<OCUpload> CREATOR = new Parcelable.Creator<OCUpload>() {
@Override
public OCUpload createFromParcel(Parcel source) {
return new OCUpload(source);
}
@Override
public OCUpload[] newArray(int size) {
return new OCUpload[size];
}
};
/**
* Reconstruct from parcel
*
* @param source The source parcel
*/
protected OCUpload(Parcel source) {
readFromParcel(source);
}
public void readFromParcel(Parcel source) {
mId = source.readLong();
mLocalPath = source.readString();
mRemotePath = source.readString();
mAccountName = source.readString();
mLocalAction = source.readInt();
mForceOverwrite = (source.readInt() == 1);
mIsCreateRemoteFolder = (source.readInt() == 1);
try {
mUploadStatus = UploadStatus.valueOf(source.readString());
} catch (IllegalArgumentException x) {
mUploadStatus = UploadStatus.UPLOAD_IN_PROGRESS;
}
mUploadEndTimeStamp = source.readLong();
try {
mLastResult = UploadResult.valueOf(source.readString());
} catch (IllegalArgumentException x) {
mLastResult = UploadResult.UNKNOWN;
}
mCreatedBy = source.readInt();
}
@Override
public int describeContents() {
return this.hashCode();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mId);
dest.writeString(mLocalPath);
dest.writeString(mRemotePath);
dest.writeString(mAccountName);
dest.writeInt(mLocalAction);
dest.writeInt(mForceOverwrite ? 1 : 0);
dest.writeInt(mIsCreateRemoteFolder ? 1 : 0);
dest.writeString(mUploadStatus.name());
dest.writeLong(mUploadEndTimeStamp);
dest.writeString(((mLastResult == null) ? "" : mLastResult.name()));
dest.writeInt(mCreatedBy);
}
enum CanUploadFileNowStatus {NOW, LATER, FILE_GONE, ERROR};
/**
* Returns true when the file may be uploaded now. This methods checks all
* restraints of the passed {@link OCUpload}, these include
* isUseWifiOnly(), check if local file exists, check if file was already
* uploaded...
*
* If return value is CanUploadFileNowStatus.NOW, uploadFile() may be
* called.
*
* @return CanUploadFileNowStatus.NOW is upload may proceed, <br>
* CanUploadFileNowStatus.LATER if upload should be performed at a
* later time, <br>
* CanUploadFileNowStatus.ERROR if a severe error happened, calling
* entity should remove upload from queue.
*
*/
private CanUploadFileNowStatus canUploadFileNow(Context context) {
if (getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) {
Log_OC.w(TAG, "Already succeeded uploadObject was again scheduled for upload. Fix that!");
return CanUploadFileNowStatus.ERROR;
}
if (!new File(getLocalPath()).exists()) {
Log_OC.d(TAG, "Do not start upload because local file does not exist.");
return CanUploadFileNowStatus.FILE_GONE;
}
return CanUploadFileNowStatus.NOW;
}
}

View file

@ -0,0 +1,59 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2016 ownCloud Inc.
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* 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.db;
import android.content.Context;
import android.preference.PreferenceManager;
/**
* Helper to simplify reading of Preferences all around the app
*/
public class PreferenceReader {
public static boolean instantPictureUploadEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
"instant_uploading",
false
);
}
public static boolean instantVideoUploadEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
"instant_video_uploading",
false
);
}
public static boolean instantPictureUploadViaWiFiOnly(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
"instant_upload_on_wifi",
false
);
}
public static boolean instantVideoUploadViaWiFiOnly(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
"instant_video_upload_on_wifi",
false
);
}
}

View file

@ -1,133 +1,155 @@
/**
* ownCloud Android client application
*
* @author Bartek Przybylski
* Copyright (C) 2011 Bartek Przybylski
* 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.db;
import android.net.Uri;
import android.provider.BaseColumns;
import com.owncloud.android.MainApp;
/**
* Meta-Class that holds various static field information
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 13;
private ProviderMeta() {
}
static public class ProviderTableMeta implements BaseColumns {
public static final String FILE_TABLE_NAME = "filelist";
public static final String OCSHARES_TABLE_NAME = "ocshares";
public static final String CAPABILITIES_TABLE_NAME = "capabilities";
public static final Uri CONTENT_URI = Uri.parse("content://"
+ MainApp.getAuthority() + "/");
public static final Uri CONTENT_URI_FILE = Uri.parse("content://"
+ MainApp.getAuthority() + "/file");
public static final Uri CONTENT_URI_DIR = Uri.parse("content://"
+ MainApp.getAuthority() + "/dir");
public static final Uri CONTENT_URI_SHARE = Uri.parse("content://"
+ MainApp.getAuthority() + "/shares");
public static final Uri CONTENT_URI_CAPABILITIES = Uri.parse("content://"
+ MainApp.getAuthority() + "/capabilities");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";
// Columns of filelist table
public static final String FILE_PARENT = "parent";
public static final String FILE_NAME = "filename";
public static final String FILE_CREATION = "created";
public static final String FILE_MODIFIED = "modified";
public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";
public static final String FILE_CONTENT_LENGTH = "content_length";
public static final String FILE_CONTENT_TYPE = "content_type";
public static final String FILE_STORAGE_PATH = "media_path";
public static final String FILE_PATH = "path";
public static final String FILE_ACCOUNT_OWNER = "file_owner";
public static final String FILE_LAST_SYNC_DATE = "last_sync_date";// _for_properties, but let's keep it as it is
public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";
public static final String FILE_ETAG = "etag";
public static final String FILE_SHARED_VIA_LINK = "share_by_link";
public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users";
public static final String FILE_PUBLIC_LINK = "public_link";
public static final String FILE_PERMISSIONS = "permissions";
public static final String FILE_REMOTE_ID = "remote_id";
public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail";
public static final String FILE_IS_DOWNLOADING= "is_downloading";
public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict";
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME
+ " collate nocase asc";
// Columns of ocshares table
public static final String OCSHARES_FILE_SOURCE = "file_source";
public static final String OCSHARES_ITEM_SOURCE = "item_source";
public static final String OCSHARES_SHARE_TYPE = "share_type";
public static final String OCSHARES_SHARE_WITH = "shate_with";
public static final String OCSHARES_PATH = "path";
public static final String OCSHARES_PERMISSIONS = "permissions";
public static final String OCSHARES_SHARED_DATE = "shared_date";
public static final String OCSHARES_EXPIRATION_DATE = "expiration_date";
public static final String OCSHARES_TOKEN = "token";
public static final String OCSHARES_SHARE_WITH_DISPLAY_NAME = "shared_with_display_name";
public static final String OCSHARES_IS_DIRECTORY = "is_directory";
public static final String OCSHARES_USER_ID = "user_id";
public static final String OCSHARES_ID_REMOTE_SHARED = "id_remote_shared";
public static final String OCSHARES_ACCOUNT_OWNER = "owner_share";
public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE
+ " collate nocase asc";
// Columns of capabilities table
public static final String CAPABILITIES_ACCOUNT_NAME = "account";
public static final String CAPABILITIES_VERSION_MAYOR = "version_mayor";
public static final String CAPABILITIES_VERSION_MINOR = "version_minor";
public static final String CAPABILITIES_VERSION_MICRO = "version_micro";
public static final String CAPABILITIES_VERSION_STRING = "version_string";
public static final String CAPABILITIES_VERSION_EDITION = "version_edition";
public static final String CAPABILITIES_CORE_POLLINTERVAL = "core_pollinterval";
public static final String CAPABILITIES_SHARING_API_ENABLED = "sharing_api_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_ENABLED = "sharing_public_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED = "sharing_public_password_enforced";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED =
"sharing_public_expire_date_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS =
"sharing_public_expire_date_days";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED =
"sharing_public_expire_date_enforced";
public static final String CAPABILITIES_SHARING_PUBLIC_SEND_MAIL = "sharing_public_send_mail";
public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload";
public static final String CAPABILITIES_SHARING_USER_SEND_MAIL = "sharing_user_send_mail";
public static final String CAPABILITIES_SHARING_RESHARING = "sharing_resharing";
public static final String CAPABILITIES_SHARING_FEDERATION_OUTGOING = "sharing_federation_outgoing";
public static final String CAPABILITIES_SHARING_FEDERATION_INCOMING = "sharing_federation_incoming";
public static final String CAPABILITIES_FILES_BIGFILECHUNKING = "files_bigfilechunking";
public static final String CAPABILITIES_FILES_UNDELETE = "files_undelete";
public static final String CAPABILITIES_FILES_VERSIONING = "files_versioning";
public static final String CAPABILITIES_DEFAULT_SORT_ORDER = CAPABILITIES_ACCOUNT_NAME
+ " collate nocase asc";
}
}
/**
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author David A. Velasco
* @author masensio
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2016 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.db;
import android.net.Uri;
import android.provider.BaseColumns;
import com.owncloud.android.MainApp;
/**
* Meta-Class that holds various static field information
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 14;
private ProviderMeta() {
}
static public class ProviderTableMeta implements BaseColumns {
public static final String FILE_TABLE_NAME = "filelist";
public static final String OCSHARES_TABLE_NAME = "ocshares";
public static final String CAPABILITIES_TABLE_NAME = "capabilities";
public static final String UPLOADS_TABLE_NAME = "list_of_uploads";
public static final Uri CONTENT_URI = Uri.parse("content://"
+ MainApp.getAuthority() + "/");
public static final Uri CONTENT_URI_FILE = Uri.parse("content://"
+ MainApp.getAuthority() + "/file");
public static final Uri CONTENT_URI_DIR = Uri.parse("content://"
+ MainApp.getAuthority() + "/dir");
public static final Uri CONTENT_URI_SHARE = Uri.parse("content://"
+ MainApp.getAuthority() + "/shares");
public static final Uri CONTENT_URI_CAPABILITIES = Uri.parse("content://"
+ MainApp.getAuthority() + "/capabilities");
public static final Uri CONTENT_URI_UPLOADS = Uri.parse("content://"
+ MainApp.getAuthority() + "/uploads");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";
// Columns of filelist table
public static final String FILE_PARENT = "parent";
public static final String FILE_NAME = "filename";
public static final String FILE_CREATION = "created";
public static final String FILE_MODIFIED = "modified";
public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";
public static final String FILE_CONTENT_LENGTH = "content_length";
public static final String FILE_CONTENT_TYPE = "content_type";
public static final String FILE_STORAGE_PATH = "media_path";
public static final String FILE_PATH = "path";
public static final String FILE_ACCOUNT_OWNER = "file_owner";
public static final String FILE_LAST_SYNC_DATE = "last_sync_date";// _for_properties, but let's keep it as it is
public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";
public static final String FILE_ETAG = "etag";
public static final String FILE_SHARED_VIA_LINK = "share_by_link";
public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users";
public static final String FILE_PUBLIC_LINK = "public_link";
public static final String FILE_PERMISSIONS = "permissions";
public static final String FILE_REMOTE_ID = "remote_id";
public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail";
public static final String FILE_IS_DOWNLOADING= "is_downloading";
public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict";
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME
+ " collate nocase asc";
// Columns of ocshares table
public static final String OCSHARES_FILE_SOURCE = "file_source";
public static final String OCSHARES_ITEM_SOURCE = "item_source";
public static final String OCSHARES_SHARE_TYPE = "share_type";
public static final String OCSHARES_SHARE_WITH = "shate_with";
public static final String OCSHARES_PATH = "path";
public static final String OCSHARES_PERMISSIONS = "permissions";
public static final String OCSHARES_SHARED_DATE = "shared_date";
public static final String OCSHARES_EXPIRATION_DATE = "expiration_date";
public static final String OCSHARES_TOKEN = "token";
public static final String OCSHARES_SHARE_WITH_DISPLAY_NAME = "shared_with_display_name";
public static final String OCSHARES_IS_DIRECTORY = "is_directory";
public static final String OCSHARES_USER_ID = "user_id";
public static final String OCSHARES_ID_REMOTE_SHARED = "id_remote_shared";
public static final String OCSHARES_ACCOUNT_OWNER = "owner_share";
public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE
+ " collate nocase asc";
// Columns of capabilities table
public static final String CAPABILITIES_ACCOUNT_NAME = "account";
public static final String CAPABILITIES_VERSION_MAYOR = "version_mayor";
public static final String CAPABILITIES_VERSION_MINOR = "version_minor";
public static final String CAPABILITIES_VERSION_MICRO = "version_micro";
public static final String CAPABILITIES_VERSION_STRING = "version_string";
public static final String CAPABILITIES_VERSION_EDITION = "version_edition";
public static final String CAPABILITIES_CORE_POLLINTERVAL = "core_pollinterval";
public static final String CAPABILITIES_SHARING_API_ENABLED = "sharing_api_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_ENABLED = "sharing_public_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED = "sharing_public_password_enforced";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED =
"sharing_public_expire_date_enabled";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS =
"sharing_public_expire_date_days";
public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED =
"sharing_public_expire_date_enforced";
public static final String CAPABILITIES_SHARING_PUBLIC_SEND_MAIL = "sharing_public_send_mail";
public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload";
public static final String CAPABILITIES_SHARING_USER_SEND_MAIL = "sharing_user_send_mail";
public static final String CAPABILITIES_SHARING_RESHARING = "sharing_resharing";
public static final String CAPABILITIES_SHARING_FEDERATION_OUTGOING = "sharing_federation_outgoing";
public static final String CAPABILITIES_SHARING_FEDERATION_INCOMING = "sharing_federation_incoming";
public static final String CAPABILITIES_FILES_BIGFILECHUNKING = "files_bigfilechunking";
public static final String CAPABILITIES_FILES_UNDELETE = "files_undelete";
public static final String CAPABILITIES_FILES_VERSIONING = "files_versioning";
public static final String CAPABILITIES_DEFAULT_SORT_ORDER = CAPABILITIES_ACCOUNT_NAME
+ " collate nocase asc";
//Columns of Uploads table
public static final String UPLOADS_LOCAL_PATH = "local_path";
public static final String UPLOADS_REMOTE_PATH = "remote_path";
public static final String UPLOADS_ACCOUNT_NAME = "account_name";
public static final String UPLOADS_FILE_SIZE = "file_size";
public static final String UPLOADS_STATUS = "status";
public static final String UPLOADS_LOCAL_BEHAVIOUR = "local_behaviour";
public static final String UPLOADS_UPLOAD_TIME = "upload_time";
public static final String UPLOADS_FORCE_OVERWRITE = "force_overwrite";
public static final String UPLOADS_IS_CREATE_REMOTE_FOLDER = "is_create_remote_folder";
public static final String UPLOADS_UPLOAD_END_TIMESTAMP = "upload_end_timestamp";
public static final String UPLOADS_LAST_RESULT = "last_result";
public static final String UPLOADS_CREATED_BY = "created_by";
public static final String UPLOADS_DEFAULT_SORT_ORDER = ProviderTableMeta._ID + " collate nocase desc";
}
}

View file

@ -0,0 +1,118 @@
/**
* ownCloud Android client application
*
* @author masensio
* Copyright (C) 2016 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.db;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
public enum UploadResult {
UNKNOWN(-1),
UPLOADED(0),
NETWORK_CONNECTION(1),
CREDENTIAL_ERROR(2),
FOLDER_ERROR(3),
CONFLICT_ERROR(4),
FILE_ERROR(5),
PRIVILEDGES_ERROR(6),
CANCELLED(7),
FILE_NOT_FOUND(8),
DELAYED_FOR_WIFI(9),
SERVICE_INTERRUPTED(10);
private final int value;
UploadResult(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static UploadResult fromValue(int value) {
switch (value) {
case -1:
return UNKNOWN;
case 0:
return UPLOADED;
case 1:
return NETWORK_CONNECTION;
case 2:
return CREDENTIAL_ERROR;
case 3:
return FOLDER_ERROR;
case 4:
return CONFLICT_ERROR;
case 5:
return FILE_ERROR;
case 6:
return PRIVILEDGES_ERROR;
case 7:
return CANCELLED;
case 8:
return FILE_NOT_FOUND;
case 9:
return DELAYED_FOR_WIFI;
case 10:
return SERVICE_INTERRUPTED;
}
return null;
}
public static UploadResult fromOperationResult(RemoteOperationResult result){
// messy :(
switch (result.getCode()){
case OK:
return UPLOADED;
case NO_NETWORK_CONNECTION:
case HOST_NOT_AVAILABLE:
case TIMEOUT:
case WRONG_CONNECTION:
case INCORRECT_ADDRESS:
case SSL_ERROR:
case SSL_RECOVERABLE_PEER_UNVERIFIED:
return NETWORK_CONNECTION;
case ACCOUNT_EXCEPTION:
case UNAUTHORIZED:
return CREDENTIAL_ERROR;
case FILE_NOT_FOUND:
return FOLDER_ERROR;
case LOCAL_FILE_NOT_FOUND:
return FILE_NOT_FOUND;
case CONFLICT:
return CONFLICT_ERROR;
case LOCAL_STORAGE_NOT_COPIED:
return FILE_ERROR;
case FORBIDDEN:
return PRIVILEDGES_ERROR;
case CANCELLED:
return CANCELLED;
case DELAYED_FOR_WIFI:
return DELAYED_FOR_WIFI;
case UNKNOWN_ERROR:
if (result.getException() instanceof java.io.FileNotFoundException) {
return FILE_ERROR;
}
return UNKNOWN;
default:
return UNKNOWN;
}
}
}

View file

@ -20,26 +20,23 @@
package com.owncloud.android.files;
import java.util.ArrayList;
import java.util.List;
import android.accounts.Account;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
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.files.services.FileUploader;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
import com.owncloud.android.ui.activity.ComponentsGetter;
import java.util.ArrayList;
import java.util.List;
/**
* Filters out the file actions available in a given {@link Menu} for a given {@link OCFile}
* according to the current state of the latest.

View file

@ -29,6 +29,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Parcelable;
import android.support.v4.app.DialogFragment;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
@ -36,7 +37,9 @@ import android.widget.Toast;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.utils.Log_OC;
@ -50,7 +53,6 @@ import com.owncloud.android.ui.activity.ShareActivity;
import com.owncloud.android.ui.dialog.ShareLinkToDialog;
import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
import java.util.List;
/**
@ -58,8 +60,8 @@ import java.util.List;
*/
public class FileOperationsHelper {
private static final String TAG = FileOperationsHelper.class.getName();
private static final String TAG = FileOperationsHelper.class.getSimpleName();
private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
protected FileActivity mFileActivity = null;
@ -91,9 +93,8 @@ public class FileOperationsHelper {
);
if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
intentForGuessedMimeType.
setDataAndType(Uri.parse("file://"+ encodedStoragePath),
guessedMimeType);
intentForGuessedMimeType.setDataAndType(Uri.parse("file://" +
encodedStoragePath), guessedMimeType);
intentForGuessedMimeType.setFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@ -235,7 +236,7 @@ public class FileOperationsHelper {
if (file != null) {
// TODO check capability?
mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
getString(R.string.wait_a_moment));
getString(R.string.wait_a_moment));
Intent service = new Intent(mFileActivity, OperationsService.class);
service.setAction(OperationsService.ACTION_CREATE_SHARE_WITH_SHAREE);
@ -323,7 +324,7 @@ public class FileOperationsHelper {
*/
public void showShareFile(OCFile file){
Intent intent = new Intent(mFileActivity, ShareActivity.class);
intent.putExtra(mFileActivity.EXTRA_FILE, file);
intent.putExtra(mFileActivity.EXTRA_FILE, (Parcelable) file);
intent.putExtra(mFileActivity.EXTRA_ACCOUNT, mFileActivity.getAccount());
mFileActivity.startActivity(intent);
@ -342,8 +343,8 @@ public class FileOperationsHelper {
SharePasswordDialogFragment dialog =
SharePasswordDialogFragment.newInstance(file, createShare);
dialog.show(
mFileActivity.getSupportFragmentManager(),
SharePasswordDialogFragment.PASSWORD_FRAGMENT
mFileActivity.getSupportFragmentManager(),
SharePasswordDialogFragment.PASSWORD_FRAGMENT
);
}
@ -475,7 +476,7 @@ public class FileOperationsHelper {
intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
getString(R.string.wait_a_moment));
getString(R.string.wait_a_moment));
} else {
Intent intent = new Intent(mFileActivity, OperationsService.class);
@ -630,4 +631,19 @@ public class FileOperationsHelper {
return false;
}
/**
* Starts a check of the currenlty stored credentials for the given account.
*
* @param account OC account which credentials will be checked.
*/
public void checkCurrentCredentials(Account account) {
Intent service = new Intent(mFileActivity, OperationsService.class);
service.setAction(OperationsService.ACTION_CHECK_CURRENT_CREDENTIALS);
service.putExtra(OperationsService.EXTRA_ACCOUNT, account);
mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
mFileActivity.showLoadingDialog(
mFileActivity.getApplicationContext().getString(R.string.wait_checking_credentials)
);
}
}

View file

@ -1,35 +1,26 @@
/**
* ownCloud Android client application
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2016 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 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/>.
* 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.files;
import java.io.File;
import com.owncloud.android.MainApp;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.db.DbHandler;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.utils.FileStorageUtils;
import android.Manifest;
import android.accounts.Account;
import android.content.BroadcastReceiver;
@ -37,13 +28,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.preference.PreferenceManager;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.support.v4.content.ContextCompat;
import android.webkit.MimeTypeMap;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.db.PreferenceReader;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.utils.FileStorageUtils;
public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
@ -52,40 +47,47 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
// Image action
// Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
// Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
// Officially supported action since SDK 14:
// http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
// Video action
// Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_VIDEO
// Officially supported action since SDK 14:
// http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_VIDEO
private static String NEW_VIDEO_ACTION = "android.hardware.action.NEW_VIDEO";
@Override
public void onReceive(Context context, Intent intent) {
Log_OC.d(TAG, "Received: " + intent.getAction());
if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
handleConnectivityAction(context, intent);
}else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
handleNewPictureAction(context, intent);
if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
handleNewPictureAction(context, intent);
Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
} else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
handleNewPictureAction(context, intent);
handleNewPictureAction(context, intent);
Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
} else if (intent.getAction().equals(NEW_VIDEO_ACTION)) {
Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_VIDEO");
handleNewVideoAction(context, intent);
Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_VIDEO");
} else {
Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
Log_OC.e(TAG, "Incorrect intent received: " + intent.getAction());
}
}
/**
* Because we support NEW_PHOTO_ACTION and NEW_PHOTO_ACTION_UNOFFICIAL it can happen that
* handleNewPictureAction is called twice for the same photo. Use this simple static variable to
* remember last uploaded photo to filter duplicates. Must not be null!
*/
static String lastUploadedPhotoPath = "";
private void handleNewPictureAction(Context context, Intent intent) {
Cursor c = null;
String file_path = null;
String file_name = null;
String mime_type = null;
Log_OC.w(TAG, "New photo received");
if (!instantPictureUploadEnabled(context)) {
Log_OC.i(TAG, "New photo received");
if (!PreferenceReader.instantPictureUploadEnabled(context)) {
Log_OC.d(TAG, "Instant picture upload disabled, ignoring new picture");
return;
}
@ -96,7 +98,8 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
return;
}
String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE };
String[] CONTENT_PROJECTION = {
Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE };
int permissionCheck = ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_EXTERNAL_STORAGE);
@ -115,44 +118,43 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE));
c.close();
Log_OC.d(TAG, file_path + "");
// save always temporally the picture to upload
DbHandler db = new DbHandler(context);
db.putFileForLater(file_path, account.name, null);
db.close();
if (!isOnline(context) || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
if (file_path.equals(lastUploadedPhotoPath)) {
Log_OC.d(TAG, "Duplicate detected: " + file_path + ". Ignore.");
return;
}
Intent i = new Intent(context, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, account);
i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, file_name));
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
lastUploadedPhotoPath = file_path;
Log_OC.d(TAG, "Path: " + file_path + "");
// instant upload behaviour
i = addInstantUploadBehaviour(i, context);
new FileUploader.UploadRequester();
context.startService(i);
int behaviour = getUploadBehaviour(context);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
context,
account,
file_path,
FileStorageUtils.getInstantUploadFilePath(context, file_name),
behaviour,
mime_type,
true, // create parent folder if not existent
UploadFileOperation.CREATED_AS_INSTANT_PICTURE
);
}
private Intent addInstantUploadBehaviour(Intent i, Context context){
private Integer getUploadBehaviour(Context context) {
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String behaviour = appPreferences.getString("prefs_instant_behaviour", "NOTHING");
if (behaviour.equalsIgnoreCase("NOTHING")) {
Log_OC.d(TAG, "upload file and do nothing");
i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_FORGET);
return FileUploader.LOCAL_BEHAVIOUR_FORGET;
} else if (behaviour.equalsIgnoreCase("MOVE")) {
i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
Log_OC.d(TAG, "upload file and move file to oc folder");
return FileUploader.LOCAL_BEHAVIOUR_MOVE;
}
return i;
return null;
}
private void handleNewVideoAction(Context context, Intent intent) {
@ -161,9 +163,9 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
String file_name = null;
String mime_type = null;
Log_OC.w(TAG, "New video received");
if (!instantVideoUploadEnabled(context)) {
Log_OC.i(TAG, "New video received");
if (!PreferenceReader.instantVideoUploadEnabled(context)) {
Log_OC.d(TAG, "Instant video upload disabled, ignoring new video");
return;
}
@ -174,133 +176,31 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
return;
}
String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE };
String[] CONTENT_PROJECTION = {Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE,
Video.Media.SIZE};
c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
if (!c.moveToFirst()) {
Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
return;
}
}
file_path = c.getString(c.getColumnIndex(Video.Media.DATA));
file_name = c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME));
mime_type = c.getString(c.getColumnIndex(Video.Media.MIME_TYPE));
c.close();
Log_OC.d(TAG, file_path + "");
if (!isOnline(context) || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
return;
}
Intent i = new Intent(context, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, account);
i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantVideoUploadFilePath(context, file_name));
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
// instant upload behaviour
i = addInstantUploadBehaviour(i, context);
context.startService(i);
int behaviour = getUploadBehaviour(context);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
context,
account,
file_path,
FileStorageUtils.getInstantVideoUploadFilePath(context, file_name),
behaviour,
mime_type,
true, // create parent folder if not existent
UploadFileOperation.CREATED_AS_INSTANT_VIDEO
);
}
private void handleConnectivityAction(Context context, Intent intent) {
if (!instantPictureUploadEnabled(context)) {
Log_OC.d(TAG, "Instant upload disabled, don't upload anything");
return;
}
if (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context)){
Account account = AccountUtils.getCurrentOwnCloudAccount(context);
if (account == null) {
Log_OC.w(TAG, "No account found for instant upload, aborting");
return;
}
Intent i = new Intent(context, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, account);
i.putExtra(FileUploader.KEY_CANCEL_ALL, true);
context.startService(i);
}
if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
&& isOnline(context)
&& (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
DbHandler db = new DbHandler(context);
Cursor c = db.getAwaitingFiles();
if (c.moveToFirst()) {
do {
if (instantPictureUploadViaWiFiOnly(context) &&
!isConnectedViaWiFi(context)){
break;
}
String account_name = c.getString(c.getColumnIndex("account"));
String file_path = c.getString(c.getColumnIndex("path"));
File f = new File(file_path);
if (f.exists()) {
Account account = new Account(account_name, MainApp.getAccountType());
String mimeType = null;
try {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
f.getName().substring(f.getName().lastIndexOf('.') + 1));
} catch (Throwable e) {
Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName());
}
if (mimeType == null)
mimeType = "application/octet-stream";
Intent i = new Intent(context, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, account);
i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, f.getName()));
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
// instant upload behaviour
i = addInstantUploadBehaviour(i, context);
context.startService(i);
} else {
Log_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
}
} while (c.moveToNext());
}
c.close();
db.close();
}
}
public static boolean isOnline(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
public static boolean isConnectedViaWiFi(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm != null && cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
&& cm.getActiveNetworkInfo().getState() == State.CONNECTED;
}
public static boolean instantPictureUploadEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
}
public static boolean instantVideoUploadEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_uploading", false);
}
public static boolean instantPictureUploadViaWiFiOnly(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
}
public static boolean instantVideoUploadViaWiFiOnly(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_wifi", false);
}
}

View file

@ -0,0 +1,206 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* Copyright (C) 2016 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.files.services;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import com.owncloud.android.db.PreferenceReader;
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.lib.common.utils.Log_OC;
/**
* Receives all connectivity action from Android OS at all times and performs
* required OC actions. For now that are: - Signal connectivity to
* {@link FileUploader}.
*
* Later can be added: - Signal connectivity to download service, deletion
* service, ... - Handle offline mode (cf.
* https://github.com/owncloud/android/issues/162)
*
* Have fun with the comments :S
*/
public class ConnectivityActionReceiver extends BroadcastReceiver {
private static final String TAG = ConnectivityActionReceiver.class.getSimpleName();
/**
* Magic keyword, by Google.
*
* {@See http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()}
*/
private static final String UNKNOWN_SSID = "<unknown ssid>";
@Override
public void onReceive(final Context context, Intent intent) {
// LOG ALL EVENTS:
Log_OC.v(TAG, "action: " + intent.getAction());
Log_OC.v(TAG, "component: " + intent.getComponent());
Bundle extras = intent.getExtras();
if (extras != null) {
for (String key : extras.keySet()) {
Log_OC.v(TAG, "key [" + key + "]: " + extras.get(key));
}
} else {
Log_OC.v(TAG, "no extras");
}
/**
* There is an interesting mess to process WifiManager.NETWORK_STATE_CHANGED_ACTION and
* ConnectivityManager.CONNECTIVITY_ACTION in a simple and reliable way.
*
* The former triggers much more events than what we really need to know about Wifi connection.
*
* But there are annoying uncertainties about ConnectivityManager.CONNECTIVITY_ACTION due
* to the deprecation of ConnectivityManager.EXTRA_NETWORK_INFO in API level 14, and the absence
* of ConnectivityManager.EXTRA_NETWORK_TYPE until API level 17. Dear Google, how should we
* handle API levels 14 to 16?
*
* In the end maybe we need to keep in memory the current knowledge about connectivity
* and update it taking into account several Intents received in a row
*
* But first let's try something "simple" to keep a basic retry of instant uploads in
* version 1.9.2, similar to the existent until 1.9.1. To be improved.
*/
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
WifiInfo wifiInfo =
intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
String bssid =
intent.getStringExtra(WifiManager.EXTRA_BSSID);
if(networkInfo.isConnected() && // not enough; see (*) right below
wifiInfo != null &&
!UNKNOWN_SSID.equals(wifiInfo.getSSID().toLowerCase()) &&
bssid != null
) {
Log_OC.d(TAG, "WiFi connected");
wifiConnected(context);
} else {
// TODO tons of things to check to conclude disconnection;
// TODO maybe alternative commented below, based on CONNECTIVITY_ACTION is better
Log_OC.d(TAG, "WiFi disconnected ... but don't know if right now");
}
}
// (*) When WiFi is lost, an Intent with network state CONNECTED and SSID "<unknown ssid>" is
// received right before another Intent with network state DISCONNECTED; needs to
// be differentiated of a new Wifi connection.
//
// Besides, with a new connection two Intents are received, having only the second the extra
// WifiManager.EXTRA_BSSID, with the BSSID of the access point accessed.
//
// Not sure if this protocol is exact, since it's not documented. Only found mild references in
// - http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()
// - http://developer.android.com/intl/es/reference/android/net/wifi/WifiManager.html#EXTRA_BSSID
// and reproduced in Nexus 5 with Android 6.
/**
* Possible alternative attending ConnectivityManager.CONNECTIVITY_ACTION.
*
* Let's see what QA has to say
*
if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent.getParcelableExtra(
ConnectivityManager.EXTRA_NETWORK_INFO // deprecated in API 14
);
int networkType = intent.getIntExtra(
ConnectivityManager.EXTRA_NETWORK_TYPE, // only from API level 17
-1
);
boolean couldBeWifiAction =
(networkInfo == null && networkType < 0) || // cases of lack of info
networkInfo.getType() == ConnectivityManager.TYPE_WIFI ||
networkType == ConnectivityManager.TYPE_WIFI;
if (couldBeWifiAction) {
if (ConnectivityUtils.isAppConnectedViaWiFi(context)) {
Log_OC.d(TAG, "WiFi connected");
wifiConnected(context);
} else {
Log_OC.d(TAG, "WiFi disconnected");
wifiDisconnected(context);
}
} /* else, CONNECTIVIY_ACTION is (probably) about other network interface (mobile, bluetooth, ...)
}
*/
}
private void wifiConnected(Context context) {
// for the moment, only recovery of instant uploads, similar to behaviour in release 1.9.1
if (
(PreferenceReader.instantPictureUploadEnabled(context) &&
PreferenceReader.instantPictureUploadViaWiFiOnly(context)) ||
(PreferenceReader.instantVideoUploadEnabled(context) &&
PreferenceReader.instantVideoUploadViaWiFiOnly(context))
) {
Log_OC.d(TAG, "Requesting retry of instant uploads (& friends)");
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.retryFailedUploads(
context,
null,
UploadResult.NETWORK_CONNECTION // for the interrupted when Wifi fell, if any
// (side effect: any upload failed due to network error will be retried too, instant or not)
);
requester.retryFailedUploads(
context,
null,
UploadResult.DELAYED_FOR_WIFI // for the rest of enqueued when Wifi fell
);
}
}
private void wifiDisconnected(Context context) {
// TODO something smart
// NOTE: explicit cancellation of only-wifi instant uploads is not needed anymore, since currently:
// - any upload in progress will be interrupted due to the lack of connectivity while the device
// reconnects through other network interface;
// - FileUploader checks instant upload settings and connection state before executing each
// upload operation, so other pending instant uploads after the current one will not be run
// (currently are silently moved to FAILED state)
}
static public void enableActionReceiver(Context context) {
PackageManager pm = context.getPackageManager();
ComponentName compName = new ComponentName(context.getApplicationContext(), ConnectivityActionReceiver.class);
pm.setComponentEnabledSetting(compName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
static public void disableActionReceiver(Context context) {
PackageManager pm = context.getPackageManager();
ComponentName compName = new ComponentName(context.getApplicationContext(), ConnectivityActionReceiver.class);
pm.setComponentEnabledSetting(compName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2015 ownCloud Inc.
* Copyright (C) 2012-2016 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,
@ -20,36 +20,6 @@
package com.owncloud.android.files.services;
import java.io.File;
import java.util.AbstractList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
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.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.notifications.NotificationBuilderWithProgressBar;
import com.owncloud.android.notifications.NotificationDelayer;
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.operations.DownloadFileOperation;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.preview.PreviewImageActivity;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.utils.ErrorMessageAdapter;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
@ -67,6 +37,35 @@ import android.os.Process;
import android.support.v4.app.NotificationCompat;
import android.util.Pair;
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.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
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.notifications.NotificationBuilderWithProgressBar;
import com.owncloud.android.notifications.NotificationDelayer;
import com.owncloud.android.operations.DownloadFileOperation;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.preview.PreviewImageActivity;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.utils.ErrorMessageAdapter;
import java.io.File;
import java.util.AbstractList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
public class FileDownloader extends Service
implements OnDatatransferProgressListener, OnAccountsUpdateListener {
@ -81,7 +80,7 @@ public class FileDownloader extends Service
public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
private static final String TAG = "FileDownloader";
private static final String TAG = FileDownloader.class.getSimpleName();
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
@ -172,8 +171,7 @@ public class FileDownloader extends Service
newDownload.addDatatransferProgressListener(this);
newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
account, file.getRemotePath(), newDownload
);
account.name, file.getRemotePath(), newDownload);
if (putResult != null) {
String downloadKey = putResult.first;
requestedDownloads.add(downloadKey);
@ -191,7 +189,6 @@ public class FileDownloader extends Service
msg.obj = requestedDownloads;
mServiceHandler.sendMessage(msg);
}
//}
}
return START_NOT_STICKY;
@ -253,7 +250,8 @@ public class FileDownloader extends Service
* @param file A file in the queue of pending downloads
*/
public void cancel(Account account, OCFile file) {
Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.remove(account, file.getRemotePath());
Pair<DownloadFileOperation, String> removeResult =
mPendingDownloads.remove(account.name, file.getRemotePath());
DownloadFileOperation download = removeResult.first;
if (download != null) {
download.cancel();
@ -301,7 +299,7 @@ public class FileDownloader extends Service
*/
public boolean isDownloading(Account account, OCFile file) {
if (account == null || file == null) return false;
return (mPendingDownloads.contains(account, file.getRemotePath()));
return (mPendingDownloads.contains(account.name, file.getRemotePath()));
}
@ -350,7 +348,6 @@ public class FileDownloader extends Service
}
/**
* Download worker. Performs the pending downloads in the order they were requested.
@ -432,8 +429,10 @@ public class FileDownloader extends Service
} finally {
Pair<DownloadFileOperation, String> removeResult =
mPendingDownloads.removePayload(mCurrentAccount,
mCurrentDownload.getRemotePath());
mPendingDownloads.removePayload(
mCurrentAccount.name,
mCurrentDownload.getRemotePath()
);
/// notify result
notifyDownloadResult(mCurrentDownload, downloadResult);
@ -546,10 +545,7 @@ public class FileDownloader extends Service
int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker :
R.string.downloader_download_failed_ticker;
boolean needsToUpdateCredentials = (
downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
downloadResult.isIdPRedirection()
);
boolean needsToUpdateCredentials = (ResultCode.UNAUTHORIZED.equals(downloadResult.getCode()));
tickerId = (needsToUpdateCredentials) ?
R.string.downloader_download_failed_credentials_error : tickerId;
@ -653,6 +649,6 @@ public class FileDownloader extends Service
*/
private void cancelDownloadsForAccount(Account account) {
// Cancel pending downloads
mPendingDownloads.remove(account);
mPendingDownloads.remove(account.name);
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -20,7 +20,6 @@
package com.owncloud.android.files.services;
import android.accounts.Account;
import android.util.Pair;
import com.owncloud.android.datamodel.OCFile;
@ -98,8 +97,9 @@ public class IndexedForest<V> {
}
public /* synchronized */ Pair<String, String> putIfAbsent(Account account, String remotePath, V value) {
String targetKey = buildKey(account, remotePath);
public /* synchronized */ Pair<String, String> putIfAbsent(String accountName, String remotePath, V value) {
String targetKey = buildKey(accountName, remotePath);
Node<V> valuedNode = new Node(targetKey, value);
Node<V> previousValue = mMap.putIfAbsent(
targetKey,
@ -119,7 +119,7 @@ public class IndexedForest<V> {
if (!parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
parentPath += OCFile.PATH_SEPARATOR;
}
parentKey = buildKey(account, parentPath);
parentKey = buildKey(accountName, parentPath);
parentNode = mMap.get(parentKey);
if (parentNode == null) {
parentNode = new Node(parentKey, null);
@ -135,7 +135,7 @@ public class IndexedForest<V> {
String linkedTo = OCFile.ROOT_PATH;
if (linked) {
linkedTo = parentNode.getKey().substring(account.name.length());
linkedTo = parentNode.getKey().substring(accountName.length());
}
return new Pair<String, String>(targetKey, linkedTo);
@ -143,21 +143,21 @@ public class IndexedForest<V> {
};
public Pair<V, String> removePayload(Account account, String remotePath) {
String targetKey = buildKey(account, remotePath);
public Pair<V, String> removePayload(String accountName, String remotePath) {
String targetKey = buildKey(accountName, remotePath);
Node<V> target = mMap.get(targetKey);
if (target != null) {
target.clearPayload();
if (!target.hasChildren()) {
return remove(account, remotePath);
return remove(accountName, remotePath);
}
}
return new Pair<V, String>(null, null);
}
public /* synchronized */ Pair<V, String> remove(Account account, String remotePath) {
String targetKey = buildKey(account, remotePath);
public /* synchronized */ Pair<V, String> remove(String accountName, String remotePath) {
String targetKey = buildKey(accountName, remotePath);
Node<V> firstRemoved = mMap.remove(targetKey);
String unlinkedFrom = null;
@ -180,7 +180,7 @@ public class IndexedForest<V> {
}
if (parent != null) {
unlinkedFrom = parent.getKey().substring(account.name.length());
unlinkedFrom = parent.getKey().substring(accountName.length());
}
return new Pair<V, String>(firstRemoved.getPayload(), unlinkedFrom);
@ -199,8 +199,8 @@ public class IndexedForest<V> {
}
}
public boolean contains(Account account, String remotePath) {
String targetKey = buildKey(account, remotePath);
public boolean contains(String accountName, String remotePath) {
String targetKey = buildKey(accountName, remotePath);
return mMap.containsKey(targetKey);
}
@ -213,22 +213,22 @@ public class IndexedForest<V> {
}
}
public V get(Account account, String remotePath) {
String key = buildKey(account, remotePath);
public V get(String accountName, String remotePath) {
String key = buildKey(accountName, remotePath);
return get(key);
}
/**
* Remove the elements that contains account as a part of its key
* @param account
* @param accountName
*/
public void remove(Account account){
public void remove(String accountName){
Iterator<String> it = mMap.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
Log_OC.d("IndexedForest", "Number of pending downloads= " + mMap.size());
if (key.startsWith(account.name)) {
if (key.startsWith(accountName)) {
mMap.remove(key);
}
}
@ -237,11 +237,11 @@ public class IndexedForest<V> {
/**
* Builds a key to index files
*
* @param account Account where the file to download is stored
* @param remotePath Path of the file in the server
* @param accountName Local name of the ownCloud account where the file to download is stored.
* @param remotePath Path of the file in the server.
*/
private String buildKey(Account account, String remotePath) {
return account.name + remotePath;
private String buildKey(String accountName, String remotePath) {
return accountName + remotePath;
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -21,7 +21,6 @@
package com.owncloud.android.media;
import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@ -39,14 +38,14 @@ import android.os.PowerManager;
import android.support.v7.app.NotificationCompat;
import android.widget.Toast;
import java.io.IOException;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import java.io.IOException;
/**
* Service that handles media playback, both audio and video.
@ -59,7 +58,8 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
private static final String TAG = MediaService.class.getSimpleName();
private static final String MY_PACKAGE = MediaService.class.getPackage() != null ? MediaService.class.getPackage().getName() : "com.owncloud.android.media";
private static final String MY_PACKAGE = MediaService.class.getPackage() != null ?
MediaService.class.getPackage().getName() : "com.owncloud.android.media";
/// Intent actions that we are prepared to handle
public static final String ACTION_PLAY_FILE = MY_PACKAGE + ".action.PLAY_FILE";
@ -162,7 +162,8 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
} else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
/* Added in API level 17
Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
Bitstream is conforming to the related coding standard or file spec,
but the media framework does not support the feature.
Constant Value: -1010 (0xfffffc0e)
*/
messageId = R.string.media_err_unsupported;
@ -190,7 +191,8 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
} else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
/* Added in API level 3
The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
The video is streamed and its container is not valid for progressive playback i.e the video's index
(e.g moov atom) is not at the start of the file.
Constant Value: 200 (0x000000c8)
*/
messageId = R.string.media_err_invalid_progressive_playback;
@ -203,7 +205,8 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
*/
/* MediaPlayer.MEDIA_ERROR_SERVER_DIED)
Added in API level 1
Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
Media server died. In this case, the application must release the MediaPlayer
object and instantiate a new one.
Constant Value: 100 (0x00000064)
*/
messageId = R.string.media_err_unknown;
@ -471,22 +474,26 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
} catch (SecurityException e) {
Log_OC.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e);
Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()),
Toast.LENGTH_LONG).show();
processStopRequest(true);
} catch (IOException e) {
Log_OC.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e);
Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()),
Toast.LENGTH_LONG).show();
processStopRequest(true);
} catch (IllegalStateException e) {
Log_OC.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e);
Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()),
Toast.LENGTH_LONG).show();
processStopRequest(true);
} catch (IllegalArgumentException e) {
Log_OC.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e);
Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()),
Toast.LENGTH_LONG).show();
processStopRequest(true);
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -21,16 +21,16 @@
package com.owncloud.android.media;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.media.MediaService.State;
import android.accounts.Account;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.widget.MediaController;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.media.MediaService.State;
/**
* Binder allowing client components to perform operations on on the MediaPlayer managed by a MediaService instance.
@ -94,8 +94,7 @@ public class MediaServiceBinder extends Binder implements MediaController.MediaP
public int getCurrentPosition() {
MediaPlayer currentPlayer = mService.getPlayer();
if (currentPlayer != null) {
int pos = currentPlayer.getCurrentPosition();
return pos;
return currentPlayer.getCurrentPosition();
} else {
return 0;
}
@ -105,8 +104,7 @@ public class MediaServiceBinder extends Binder implements MediaController.MediaP
public int getDuration() {
MediaPlayer currentPlayer = mService.getPlayer();
if (currentPlayer != null) {
int dur = currentPlayer.getDuration();
return dur;
return currentPlayer.getDuration();
} else {
return 0;
}

View file

@ -0,0 +1,65 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2016 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 android.accounts.Account;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import com.owncloud.android.operations.common.SyncOperation;
import java.util.ArrayList;
/**
* Checks validity of currently stored credentials for a given OC account
*/
public class CheckCurrentCredentialsOperation extends SyncOperation {
private Account mAccount = null;
public CheckCurrentCredentialsOperation(Account account) {
if (account == null) {
throw new IllegalArgumentException("NULL account");
}
mAccount = account;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
if (!getStorageManager().getAccount().name.equals(mAccount.name)) {
result = new RemoteOperationResult(new IllegalStateException(
"Account to validate is not the account connected to!")
);
} else {
RemoteOperation check = new ExistenceCheckRemoteOperation(OCFile.ROOT_PATH, false);
result = check.execute(client);
ArrayList<Object> data = new ArrayList<Object>();
data.add(mAccount);
result.setData(data);
}
return result;
}
}

View file

@ -34,6 +34,8 @@ import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import android.content.Context;
import android.net.Uri;
import org.apache.commons.httpclient.HttpStatus;
/**
* Operation to find out what authentication method requires
* the server to access files.
@ -99,7 +101,7 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation {
}
// analyze response
if (result.getCode() == ResultCode.UNAUTHORIZED) {
if (result.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
String authRequest = ((result.getAuthenticateHeader()).trim()).toLowerCase();
if (authRequest.startsWith("basic")) {
authMethod = AuthenticationMethod.BASIC_HTTP_AUTH;

View file

@ -1,43 +1,42 @@
/**
* ownCloud Android client application
* ownCloud Android client application
*
* @author David A. Velasco
* @author masensio
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* @author David A. Velasco
* @author masensio
* Copyright (C) 2012 Bartek Przybylski
* 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 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/>.
* 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 android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.resources.files.RemoteFile;
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.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.operations.common.SyncOperation;
import com.owncloud.android.utils.FileStorageUtils;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
/**
* Remote operation performing the read of remote file in the ownCloud server.
*/
@ -45,44 +44,44 @@ import android.content.Intent;
public class SynchronizeFileOperation extends SyncOperation {
private String TAG = SynchronizeFileOperation.class.getSimpleName();
private OCFile mLocalFile;
private String mRemotePath;
private OCFile mServerFile;
private Account mAccount;
private boolean mSyncFileContents;
private Context mContext;
private boolean mTransferWasRequested = false;
/**
* When 'false', uploads to the server are not done; only downloads or conflict detection.
* This is a temporal field.
/**
* When 'false', uploads to the server are not done; only downloads or conflict detection.
* This is a temporal field.
* TODO Remove when 'folder synchronization' replaces 'folder download'.
*/
*/
private boolean mAllowUploads;
/**
* Constructor for "full synchronization mode".
*
* <p/>
* Uses remotePath to retrieve all the data both in local cache and in the remote OC server
* when the operation is executed, instead of reusing {@link OCFile} instances.
*
* <p/>
* Useful for direct synchronization of a single file.
*
* @param
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param context Android context; needed to start transfers.
*
* @param
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param context Android context; needed to start transfers.
*/
public SynchronizeFileOperation(
String remotePath,
Account account,
String remotePath,
Account account,
boolean syncFileContents,
Context context) {
mRemotePath = remotePath;
mLocalFile = null;
mServerFile = null;
@ -92,33 +91,33 @@ public class SynchronizeFileOperation extends SyncOperation {
mAllowUploads = true;
}
/**
* Constructor allowing to reuse {@link OCFile} instances just queried from local cache or
* from remote OC server.
*
*
* Useful to include this operation as part of the synchronization of a folder
* (or a full account), avoiding the repetition of fetch operations (both in local database
* or remote server).
*
*
* At least one of localFile or serverFile MUST NOT BE NULL. If you don't have none of them,
* use the other constructor.
*
* @param localFile Data of file (just) retrieved from local cache/database.
* @param serverFile Data of file (just) retrieved from a remote server. If null,
* will be retrieved from network by the operation when executed.
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param context Android context; needed to start transfers.
*
* @param localFile Data of file (just) retrieved from local cache/database.
* @param serverFile Data of file (just) retrieved from a remote server. If null,
* will be retrieved from network by the operation when executed.
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param context Android context; needed to start transfers.
*/
public SynchronizeFileOperation(
OCFile localFile,
OCFile serverFile,
Account account,
OCFile serverFile,
Account account,
boolean syncFileContents,
Context context) {
mLocalFile = localFile;
mServerFile = serverFile;
if (mLocalFile != null) {
@ -137,55 +136,55 @@ public class SynchronizeFileOperation extends SyncOperation {
mContext = context;
mAllowUploads = true;
}
/**
* Temporal constructor.
*
*
* Extends the previous one to allow constrained synchronizations where uploads are never
* performed - only downloads or conflict detection.
*
*
* Do not use unless you are involved in 'folder synchronization' or 'folder download' work
* in progress.
*
*
* TODO Remove when 'folder synchronization' replaces 'folder download'.
*
* @param localFile Data of file (just) retrieved from local cache/database.
* MUSTN't be null.
* @param serverFile Data of file (just) retrieved from a remote server.
* If null, will be retrieved from network by the operation
* when executed.
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param allowUploads When 'false', uploads to the server are not done;
* only downloads or conflict detection.
* @param context Android context; needed to start transfers.
*
* @param localFile Data of file (just) retrieved from local cache/database.
* MUSTN't be null.
* @param serverFile Data of file (just) retrieved from a remote server.
* If null, will be retrieved from network by the operation
* when executed.
* @param account ownCloud account holding the file.
* @param syncFileContents When 'true', transference of data will be started by the
* operation if needed and no conflict is detected.
* @param allowUploads When 'false', uploads to the server are not done;
* only downloads or conflict detection.
* @param context Android context; needed to start transfers.
*/
public SynchronizeFileOperation(
OCFile localFile,
OCFile serverFile,
Account account,
OCFile serverFile,
Account account,
boolean syncFileContents,
boolean allowUploads,
boolean allowUploads,
Context context) {
this(localFile, serverFile, account, syncFileContents, context);
mAllowUploads = allowUploads;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
mTransferWasRequested = false;
if (mLocalFile == null) {
// Get local file from the DB
mLocalFile = getStorageManager().getFileByPath(mRemotePath);
}
if (!mLocalFile.isDown()) {
/// easy decision
requestForDownload(mLocalFile);
@ -197,13 +196,13 @@ public class SynchronizeFileOperation extends SyncOperation {
if (mServerFile == null) {
ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mRemotePath);
result = operation.execute(client);
if (result.isSuccess()){
if (result.isSuccess()) {
mServerFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
}
}
if (mServerFile != null) {
if (mServerFile != null) {
/// check changes in server and local file
boolean serverChanged = false;
@ -215,7 +214,7 @@ public class SynchronizeFileOperation extends SyncOperation {
serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));
}
boolean localChanged = (
mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()
mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()
);
/// decide action to perform depending upon changes
@ -240,7 +239,7 @@ public class SynchronizeFileOperation extends SyncOperation {
} else if (serverChanged) {
mLocalFile.setRemoteId(mServerFile.getRemoteId());
if (mSyncFileContents) {
requestForDownload(mLocalFile); // local, not server; we won't to keep
// the value of favorite!
@ -277,31 +276,24 @@ public class SynchronizeFileOperation extends SyncOperation {
return result;
}
/**
* Requests for an upload to the FileUploader service
*
* @param file OCFile object representing the file to upload
*
* @param file OCFile object representing the file to upload
*/
private void requestForUpload(OCFile file) {
Intent i = new Intent(mContext, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
i.putExtra(FileUploader.KEY_FILE, file);
/*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
// doing this we would lose the value of isFavorite in the road, and maybe
// it's not updated in the database when the FileUploader service gets it!
i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
mContext.startService(i);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadUpdate(mContext, mAccount, file, FileUploader.LOCAL_BEHAVIOUR_MOVE, true);
mTransferWasRequested = true;
}
/**
* Requests for a download to the FileDownloader service
*
* @param file OCFile object representing the file to download
*
* @param file OCFile object representing the file to download
*/
private void requestForDownload(OCFile file) {
Intent i = new Intent(mContext, FileDownloader.class);

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -50,8 +50,6 @@ import com.owncloud.android.utils.FileStorageUtils;
import java.util.concurrent.atomic.AtomicBoolean;
//import android.support.v4.content.LocalBroadcastManager;
/**
* Remote operation performing the synchronization of the list of files contained
@ -479,6 +477,18 @@ public class SynchronizeFolderOperation extends SyncOperation {
}
/**
* 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.
*/
private OCFile fillOCFile(RemoteFile remote) {
return FileStorageUtils.fillOCFile(remote);
}
/**
* Scans the default location for saving local copies of files searching for
* a 'lost' file with the same full name as the {@link com.owncloud.android.datamodel.OCFile}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -20,6 +20,37 @@
package com.owncloud.android.operations;
import android.accounts.Account;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.db.PreferenceReader;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.network.ProgressiveDataTransferer;
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.ChunkedUploadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import com.owncloud.android.operations.common.SyncOperation;
import com.owncloud.android.utils.ConnectivityUtils;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimetypeIconUtil;
import com.owncloud.android.utils.UriUtils;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.RequestEntity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -32,67 +63,116 @@ import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.RequestEntity;
import android.accounts.Account;
import android.content.Context;
import android.net.Uri;
import com.owncloud.android.MainApp;
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.OwnCloudClient;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.network.ProgressiveDataTransferer;
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.ChunkedUploadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.UriUtils;
/**
* Remote operation performing the upload of a file to an ownCloud server
* Operation performing the update in the ownCloud server
* of a file that was modified locally.
*/
public class UploadFileOperation extends RemoteOperation {
public class UploadFileOperation extends SyncOperation {
private static final String MIME_TYPE_PDF = "application/pdf";
private static final String FILE_EXTENSION_PDF = ".pdf";
public static final int CREATED_BY_USER = 0;
public static final int CREATED_AS_INSTANT_PICTURE = 1;
public static final int CREATED_AS_INSTANT_VIDEO = 2;
/**
* Checks if content provider, using the content:// scheme, returns a file with mime-type
* 'application/pdf' but file has not extension
* @param localPath Full path to a file in the local file system.
* @param mimeType MIME type of the file.
* @return true if is needed to add the pdf file extension to the file
*
* TODO - move to OCFile or Utils class
*/
private static boolean isPdfFileFromContentProviderWithoutExtension(String localPath,
String mimeType) {
return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) &&
mimeType.equals(MIME_TYPE_PDF) &&
!localPath.endsWith(FILE_EXTENSION_PDF);
}
public static OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) {
// MIME type
if (mimeType == null || mimeType.length() <= 0) {
mimeType = MimetypeIconUtil.getBestMimeTypeByFilename(localPath);
}
// TODO - this is a horrible special case that should not be handled this way
if (isPdfFileFromContentProviderWithoutExtension(localPath, mimeType)){
remotePath += FILE_EXTENSION_PDF;
}
OCFile newFile = new OCFile(remotePath);
newFile.setStoragePath(localPath);
newFile.setLastSyncDateForProperties(0);
newFile.setLastSyncDateForData(0);
// size
if (localPath != null && localPath.length() > 0) {
File localFile = new File(localPath);
newFile.setFileLength(localFile.length());
newFile.setLastSyncDateForData(localFile.lastModified());
} // don't worry about not assigning size, the problems with localPath
// are checked when the UploadFileOperation instance is created
newFile.setMimetype(mimeType);
return newFile;
}
private static final String TAG = UploadFileOperation.class.getSimpleName();
private Account mAccount;
/**
* OCFile which is to be uploaded.
*/
private OCFile mFile;
/**
* Original OCFile which is to be uploaded in case file had to be renamed
* (if forceOverwrite==false and remote file already exists).
*/
private OCFile mOldFile;
private String mRemotePath = null;
private boolean mChunked = false;
private boolean mIsInstant = false;
private boolean mRemoteFolderToBeCreated = false;
private boolean mForceOverwrite = false;
private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
private int mCreatedBy = CREATED_BY_USER;
private boolean mWasRenamed = false;
private String mOriginalFileName = null;
private long mOCUploadId = -1;
/**
* Local path to file which is to be uploaded (before any possible renaming or moving).
*/
private String mOriginalStoragePath = null;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
private OnRenameListener mRenameUploadListener;
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
private final AtomicBoolean mUploadStarted = new AtomicBoolean(false);
private Context mContext;
private UploadRemoteFileOperation mUploadOperation;
protected RequestEntity mEntity = null;
public UploadFileOperation( Account account,
OCFile file,
boolean chunked,
boolean isInstant,
boolean forceOverwrite,
int localBehaviour,
Context context) {
public UploadFileOperation(Account account,
OCFile file,
boolean chunked,
boolean forceOverwrite,
int localBehaviour,
Context context
) {
if (account == null)
throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation " +
"creation");
@ -108,7 +188,6 @@ public class UploadFileOperation extends RemoteOperation {
mFile = file;
mRemotePath = file.getRemotePath();
mChunked = chunked;
mIsInstant = isInstant;
mForceOverwrite = forceOverwrite;
mLocalBehaviour = localBehaviour;
mOriginalStoragePath = mFile.getStoragePath();
@ -116,6 +195,42 @@ public class UploadFileOperation extends RemoteOperation {
mContext = context;
}
public UploadFileOperation(Account account,
OCUpload upload,
boolean chunked,
boolean forceOverwrite,
int localBehaviour,
Context context
) {
if (account == null)
throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation " +
"creation");
if (upload == null)
throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
if (upload.getLocalPath() == null || upload.getLocalPath().length() <= 0) {
throw new IllegalArgumentException(
"Illegal file in UploadFileOperation; storage path invalid: "
+ upload.getLocalPath());
}
mAccount = account;
mFile = obtainNewOCFileToUpload(
upload.getRemotePath(),
upload.getLocalPath(),
upload.getMimeType()
);
mRemotePath = upload.getRemotePath();
mChunked = chunked;
mForceOverwrite = forceOverwrite;
mLocalBehaviour = localBehaviour;
mOriginalStoragePath = mFile.getStoragePath();
mOriginalFileName = mFile.getFileName();
mContext = context;
mOCUploadId = upload.getUploadId();
mCreatedBy = upload.getCreadtedBy();
mRemoteFolderToBeCreated = upload.isCreateRemoteFolder();
}
public Account getAccount() {
return mAccount;
}
@ -128,6 +243,10 @@ public class UploadFileOperation extends RemoteOperation {
return mFile;
}
/**
* If remote file was renamed, return original OCFile which was uploaded. Is
* null is file was not renamed.
*/
public OCFile getOldFile() {
return mOldFile;
}
@ -148,30 +267,48 @@ public class UploadFileOperation extends RemoteOperation {
return mFile.getMimetype();
}
public boolean isInstant() {
return mIsInstant;
}
public boolean isRemoteFolderToBeCreated() {
return mRemoteFolderToBeCreated;
public int getLocalBehaviour() {
return mLocalBehaviour;
}
public void setRemoteFolderToBeCreated() {
mRemoteFolderToBeCreated = true;
}
public boolean getForceOverwrite() {
return mForceOverwrite;
}
public boolean wasRenamed() {
return mWasRenamed;
}
public void setCreatedBy(int createdBy) {
mCreatedBy = createdBy;
if (createdBy < CREATED_BY_USER || CREATED_AS_INSTANT_VIDEO < createdBy) {
mCreatedBy = CREATED_BY_USER;
}
}
public int getCreatedBy () {
return mCreatedBy;
}
public boolean isInstantPicture() {
return mCreatedBy == CREATED_AS_INSTANT_PICTURE;
}
public boolean isInstantVideo() {
return mCreatedBy == CREATED_AS_INSTANT_VIDEO;
}
public void setOCUploadId(long id){
mOCUploadId = id;
}
public long getOCUploadId() {
return mOCUploadId;
}
public Set<OnDatatransferProgressListener> getDataTransferListeners() {
return mDataTransferListeners;
}
public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.add(listener);
@ -179,8 +316,11 @@ public class UploadFileOperation extends RemoteOperation {
if (mEntity != null) {
((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
}
if(mUploadOperation != null){
mUploadOperation.addDatatransferProgressListener(listener);
}
}
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
synchronized (mDataTransferListeners) {
mDataTransferListeners.remove(listener);
@ -188,125 +328,87 @@ public class UploadFileOperation extends RemoteOperation {
if (mEntity != null) {
((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
}
if(mUploadOperation != null){
mUploadOperation.removeDatatransferProgressListener(listener);
}
}
public void addRenameUploadListener (OnRenameListener listener) {
mRenameUploadListener = listener;
}
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
mCancellationRequested.set(false);
mUploadStarted.set(true);
RemoteOperationResult result = null;
boolean localCopyPassed = false, nameCheckPassed = false;
File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
try {
// / rename the file to upload, if necessary
/// Check that connectivity conditions are met and delays the upload otherwise
if (delayForWifi()) {
Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath());
return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI);
}
/// check if the file continues existing before schedule the operation
if (!originalFile.exists()) {
Log_OC.d(TAG, mOriginalStoragePath.toString() + " not exists anymore");
return new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
}
/// check the existence of the parent folder for the file to upload
String remoteParentPath = new File(getRemotePath()).getParent();
remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
result = grantFolderExistence(remoteParentPath, client);
if (!result.isSuccess()) {
return result;
}
/// set parent local id in uploading file
OCFile parent = getStorageManager().getFileByPath(remoteParentPath);
mFile.setParentId(parent.getFileId());
/// automatic rename of file to upload in case of name collision in server
Log_OC.d(TAG, "Checking name collision in server");
if (!mForceOverwrite) {
String remotePath = getAvailableRemotePath(client, mRemotePath);
mWasRenamed = !remotePath.equals(mRemotePath);
if (mWasRenamed) {
createNewOCFile(remotePath);
Log_OC.d(TAG, "File renamed as " + remotePath);
}
mRemotePath = remotePath;
mRenameUploadListener.onRenameUpload();
}
nameCheckPassed = true;
String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // /
// not
// before
// getAvailableRemotePath()
// !!!
if (mCancellationRequested.get()) {
throw new OperationCancelledException();
}
String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
expectedFile = new File(expectedPath);
// check location of local file; if not the expected, copy to a
// temporal file before upload (if COPY is the expected behaviour)
if (!mOriginalStoragePath.equals(expectedPath) &&
mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
/// copy the file locally before uploading
if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY &&
!mOriginalStoragePath.equals(expectedPath)) {
if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
return result; // error condition when the file should be
// copied
String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
mFile.setStoragePath(temporalPath);
temporalFile = new File(temporalPath);
} else {
String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) +
mFile.getRemotePath();
mFile.setStoragePath(temporalPath);
temporalFile = new File(temporalPath);
File temporalParent = temporalFile.getParentFile();
temporalParent.mkdirs();
if (!temporalParent.isDirectory()) {
throw new IOException("Unexpected error: parent directory could not be created");
}
temporalFile.createNewFile();
if (!temporalFile.isFile()) {
throw new IOException("Unexpected error: target file could not be created");
}
InputStream in = null;
OutputStream out = null;
try {
// In case document provider schema as 'content://'
if (mOriginalStoragePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
Uri uri = Uri.parse(mOriginalStoragePath);
in = MainApp.getAppContext().getContentResolver().openInputStream(uri);
out = new FileOutputStream(temporalFile);
int nRead;
byte[] data = new byte[16384];
while (!mCancellationRequested.get() &&
(nRead = in.read(data, 0, data.length)) != -1) {
out.write(data, 0, nRead);
}
out.flush();
} else {
if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
// weird
// but
// possible
// situation
in = new FileInputStream(originalFile);
out = new FileOutputStream(temporalFile);
byte[] buf = new byte[1024];
int len;
while (!mCancellationRequested.get() && (len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
if (mCancellationRequested.get()) {
result = new RemoteOperationResult(new OperationCancelledException());
}
} catch (Exception e) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
return result;
} finally {
try {
if (in != null)
in.close();
} catch (Exception e) {
Log_OC.d(TAG, "Weird exception while closing input stream for " +
mOriginalStoragePath + " (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);
}
}
result = copy(originalFile, temporalFile);
if (result != null) {
return result;
}
}
localCopyPassed = (result == null);
if (mCancellationRequested.get()) {
throw new OperationCancelledException();
}
/// perform the upload
if ( mChunked &&
@ -322,6 +424,7 @@ public class UploadFileOperation extends RemoteOperation {
while (listener.hasNext()) {
mUploadOperation.addDatatransferProgressListener(listener.next());
}
if (mCancellationRequested.get()) {
throw new OperationCancelledException();
}
@ -335,53 +438,17 @@ public class UploadFileOperation extends RemoteOperation {
mFile.setStoragePath("");
} else {
mFile.setStoragePath(expectedPath);
File fileToMove = null;
if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
// ; see where temporalFile was
// set
File fileToMove;
if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
fileToMove = temporalFile;
} else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
} else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
fileToMove = originalFile;
}
if (!expectedFile.equals(fileToMove)) {
File expectedFolder = expectedFile.getParentFile();
expectedFolder.mkdirs();
if (expectedFolder.isDirectory()){
if (!fileToMove.renameTo(expectedFile)){
// try to copy and then delete
expectedFile.createNewFile();
FileChannel inChannel = new FileInputStream(fileToMove).getChannel();
FileChannel outChannel = new FileOutputStream(expectedFile).getChannel();
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
fileToMove.delete();
} catch (Exception e){
mFile.setStoragePath(null); // forget the local file
// by now, treat this as a success; the file was
// uploaded; the user won't like that the local file
// is not linked, but this should be a very rare
// fail;
// the best option could be show a warning message
// (but not a fail)
// result = new
// RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
// return result;
}
finally {
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
}
}
} else {
mFile.setStoragePath(null);
}
}
move(fileToMove, expectedFile);
}
FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath());
FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
} else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
}
@ -390,6 +457,7 @@ public class UploadFileOperation extends RemoteOperation {
result = new RemoteOperationResult(e);
} finally {
mUploadStarted.set(false);
if (temporalFile != null && !originalFile.equals(temporalFile)) {
temporalFile.delete();
}
@ -401,16 +469,14 @@ public class UploadFileOperation extends RemoteOperation {
result.getLogMessage());
} else {
if (result.getException() != null) {
String complement = "";
if (!nameCheckPassed) {
complement = " (while checking file existence in server)";
} else if (!localCopyPassed) {
complement = " (while copying local file to " +
FileStorageUtils.getSavePath(mAccount.name)
+ ")";
if(result.isCancelled()){
Log_OC.w(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
": " + result.getLogMessage());
} else {
Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
": " + result.getLogMessage(), result.getException());
}
Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
": " + result.getLogMessage() + complement, result.getException());
} else {
Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
": " + result.getLogMessage());
@ -418,9 +484,93 @@ public class UploadFileOperation extends RemoteOperation {
}
}
if (result.isSuccess()) {
saveUploadedFile(client);
} else if (result.getCode() == ResultCode.SYNC_CONFLICT) {
getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
}
return result;
}
/**
* Checks origin of current upload and network type to decide if should be delayed, according to
* current user preferences.
*
* @return 'True' if the upload was delayed until WiFi connectivity is available, 'false' otherwise.
*/
private boolean delayForWifi() {
boolean delayInstantPicture = (
isInstantPicture() && PreferenceReader.instantPictureUploadViaWiFiOnly(mContext)
);
boolean delayInstantVideo = (
isInstantVideo() && PreferenceReader.instantVideoUploadViaWiFiOnly(mContext)
);
return (
(delayInstantPicture || delayInstantVideo) &&
!ConnectivityUtils.isAppConnectedViaWiFi(mContext)
);
}
/**
* Checks the existence of the folder where the current file will be uploaded both
* in the remote server and in the local database.
* <p/>
* If the upload is set to enforce the creation of the folder, the method tries to
* create it both remote and locally.
*
* @param pathToGrant Full remote path whose existence will be granted.
* @return An {@link OCFile} instance corresponding to the folder where the file
* will be uploaded.
*/
private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) {
RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, mContext, false);
RemoteOperationResult result = operation.execute(client);
if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
result = syncOp.execute(client, getStorageManager());
}
if (result.isSuccess()) {
OCFile parentDir = getStorageManager().getFileByPath(pathToGrant);
if (parentDir == null) {
parentDir = createLocalFolder(pathToGrant);
}
if (parentDir != null) {
result = new RemoteOperationResult(ResultCode.OK);
} else {
result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
}
}
return result;
}
private OCFile createLocalFolder(String remotePath) {
String parentPath = new File(remotePath).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ?
parentPath : parentPath + OCFile.PATH_SEPARATOR;
OCFile parent = getStorageManager().getFileByPath(parentPath);
if (parent == null) {
parent = createLocalFolder(parentPath);
}
if (parent != null) {
OCFile createdFolder = new OCFile(remotePath);
createdFolder.setMimetype("DIR");
createdFolder.setParentId(parent.getFileId());
getStorageManager().saveFile(createdFolder);
return createdFolder;
}
return null;
}
/**
* Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false.
* New file is stored as mFile, original as mOldFile.
* @param newRemotePath new remote path
*/
private void createNewOCFile(String newRemotePath) {
// a new OCFile instance must be created for a new remote path
OCFile newFile = new OCFile(newRemotePath);
@ -429,7 +579,8 @@ public class UploadFileOperation extends RemoteOperation {
newFile.setMimetype(mFile.getMimetype());
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
newFile.setModificationTimestampAtLastSyncForData(
mFile.getModificationTimestampAtLastSyncForData());
mFile.getModificationTimestampAtLastSyncForData()
);
newFile.setEtag(mFile.getEtag());
newFile.setFavorite(mFile.isFavorite());
newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
@ -443,12 +594,12 @@ public class UploadFileOperation extends RemoteOperation {
/**
* Checks if remotePath does not exist in the server and returns it, or adds
* a suffix to it in order to avoid the server file is overwritten.
*
*
* @param wc
* @param remotePath
* @return
*/
private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) throws Exception {
private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) {
boolean check = existsFile(wc, remotePath);
if (!check) {
return remotePath;
@ -486,11 +637,224 @@ public class UploadFileOperation extends RemoteOperation {
RemoteOperationResult result = existsOperation.execute(client);
return result.isSuccess();
}
/**
* Allows to cancel the actual upload operation. If actual upload operating
* is in progress it is cancelled, if upload preparation is being performed
* upload will not take place.
*/
public void cancel() {
mCancellationRequested = new AtomicBoolean(true);
if (mUploadOperation != null) {
if (mUploadOperation == null) {
if (mUploadStarted.get()) {
Log_OC.d(TAG, "Cancelling upload during upload preparations.");
mCancellationRequested.set(true);
} else {
Log_OC.e(TAG, "No upload in progress. This should not happen.");
}
} else {
Log_OC.d(TAG, "Cancelling upload during actual upload operation.");
mUploadOperation.cancel();
}
}
/**
* As soon as this method return true, upload can be cancel via cancel().
*/
public boolean isUploadInProgress() {
return mUploadStarted.get();
}
/**
* TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
* TODO use Exceptions instead
*
* @param sourceFile Source file to copy.
* @param targetFile Target location to copy the file.
* @return {@link RemoteOperationResult}
* @throws IOException
*/
private RemoteOperationResult copy(File sourceFile, File targetFile) throws IOException {
Log_OC.d(TAG, "Copying local file");
RemoteOperationResult result = null;
if (FileStorageUtils.getUsableSpace(mAccount.name) < sourceFile.length()) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
return result; // error condition when the file should be copied
} else {
Log_OC.d(TAG, "Creating temporal folder");
File temporalParent = targetFile.getParentFile();
temporalParent.mkdirs();
if (!temporalParent.isDirectory()) {
throw new IOException(
"Unexpected error: parent directory could not be created");
}
Log_OC.d(TAG, "Creating temporal file");
targetFile.createNewFile();
if (!targetFile.isFile()) {
throw new IOException(
"Unexpected error: target file could not be created");
}
Log_OC.d(TAG, "Copying file contents");
InputStream in = null;
OutputStream out = null;
try {
if (!mOriginalStoragePath.equals(targetFile.getAbsolutePath())) {
// In case document provider schema as 'content://'
if (mOriginalStoragePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
Uri uri = Uri.parse(mOriginalStoragePath);
in = mContext.getContentResolver().openInputStream(uri);
} else {
in = new FileInputStream(sourceFile);
}
out = new FileOutputStream(targetFile);
int nRead;
byte[] buf = new byte[4096];
while (!mCancellationRequested.get() &&
(nRead = in.read(buf)) > -1) {
out.write(buf, 0, nRead);
}
out.flush();
} // else: weird but possible situation, nothing to copy
if (mCancellationRequested.get()) {
result = new RemoteOperationResult(new OperationCancelledException());
return result;
}
} catch (Exception e) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
return result;
} finally {
try {
if (in != null)
in.close();
} catch (Exception e) {
Log_OC.d(TAG, "Weird exception while closing input stream for " +
mOriginalStoragePath + " (ignoring)", e);
}
try {
if (out != null)
out.close();
} catch (Exception e) {
Log_OC.d(TAG, "Weird exception while closing output stream for " +
targetFile.getAbsolutePath() + " (ignoring)", e);
}
}
}
return result;
}
/**
* TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
* TODO use Exceptions instead
*
* TODO refactor both this and 'copy' in a single method
*
* @param sourceFile Source file to move.
* @param targetFile Target location to move the file.
* @return {@link RemoteOperationResult}
* @throws IOException
*/
private void move(File sourceFile, File targetFile) throws IOException {
if (!targetFile.equals(sourceFile)) {
File expectedFolder = targetFile.getParentFile();
expectedFolder.mkdirs();
if (expectedFolder.isDirectory()){
if (!sourceFile.renameTo(targetFile)){
// try to copy and then delete
targetFile.createNewFile();
FileChannel inChannel = new FileInputStream(sourceFile).getChannel();
FileChannel outChannel = new FileOutputStream(targetFile).getChannel();
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
sourceFile.delete();
} catch (Exception e){
mFile.setStoragePath(""); // forget the local file
// by now, treat this as a success; the file was uploaded
// the best option could be show a warning message
}
finally {
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
}
}
} else {
mFile.setStoragePath("");
}
}
}
/**
* Saves a OC File after a successful upload.
* <p/>
* A PROPFIND is necessary to keep the props in the local database
* synchronized with the server, specially the modification time and Etag
* (where available)
* <p/>
*/
private void saveUploadedFile(OwnCloudClient client) {
OCFile file = mFile;
if (file.fileExists()) {
file = getStorageManager().getFileById(file.getFileId());
}
long syncDate = System.currentTimeMillis();
file.setLastSyncDateForData(syncDate);
// new PROPFIND to keep data consistent with server
// in theory, should return the same we already have
// TODO from the appropriate OC server version, get data from last PUT response headers, instead
// TODO of a new PROPFIND; the latter may fail, specially for chunked uploads
ReadRemoteFileOperation operation = new ReadRemoteFileOperation(getRemotePath());
RemoteOperationResult result = operation.execute(client);
if (result.isSuccess()) {
updateOCFile(file, (RemoteFile) result.getData().get(0));
file.setLastSyncDateForProperties(syncDate);
} else {
Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt...");
}
if (mWasRenamed) {
OCFile oldFile = mOldFile;
if (oldFile.fileExists()) {
oldFile.setStoragePath(null);
getStorageManager().saveFile(oldFile);
getStorageManager().saveConflict(oldFile, null);
}
// else: it was just an automatic renaming due to a name
// coincidence; nothing else is needed, the storagePath is right
// in the instance returned by mCurrentUpload.getFile()
}
file.setNeedsUpdateThumbnail(true);
getStorageManager().saveFile(file);
getStorageManager().saveConflict(file, null);
FileDataStorageManager.triggerMediaScan(file.getStoragePath());
}
private void updateOCFile(OCFile file, RemoteFile remoteFile) {
file.setCreationTimestamp(remoteFile.getCreationTimestamp());
file.setFileLength(remoteFile.getLength());
file.setMimetype(remoteFile.getMimeType());
file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
file.setEtag(remoteFile.getEtag());
file.setRemoteId(remoteFile.getRemoteId());
}
public interface OnRenameListener {
void onRenameUpload();
}
}

View file

@ -3,8 +3,9 @@
*
* @author Bartek Przybylski
* @author David A. Velasco
* @author masensio
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -43,6 +44,7 @@ import android.text.TextUtils;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.db.ProviderMeta;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
import com.owncloud.android.lib.common.accounts.AccountUtils;
@ -65,9 +67,12 @@ public class FileContentProvider extends ContentProvider {
private static final int ROOT_DIRECTORY = 3;
private static final int SHARES = 4;
private static final int CAPABILITIES = 5;
private static final int UPLOADS = 6;
private static final String TAG = FileContentProvider.class.getSimpleName();
private final String MAX_SUCCESSFUL_UPLOADS = "30";
private UriMatcher mUriMatcher;
@Override
@ -89,25 +94,25 @@ public class FileContentProvider extends ContentProvider {
private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) {
int count = 0;
switch (mUriMatcher.match(uri)) {
case SINGLE_FILE:
Cursor c = query(db, uri, null, where, whereArgs, null);
String remoteId = "";
if (c != null && c.moveToFirst()) {
remoteId = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID));
//ThumbnailsCacheManager.removeFileFromCache(remoteId);
c.close();
}
Log_OC.d(TAG, "Removing FILE " + remoteId);
case SINGLE_FILE:
Cursor c = query(db, uri, null, where, whereArgs, null);
String remoteId = "";
if (c != null && c.moveToFirst()) {
remoteId = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID));
//ThumbnailsCacheManager.removeFileFromCache(remoteId);
c.close();
}
Log_OC.d(TAG, "Removing FILE " + remoteId);
count = db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ")" : ""), whereArgs);
break;
case DIRECTORY:
// deletion of folder is recursive
count = db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ")" : ""), whereArgs);
break;
case DIRECTORY:
// deletion of folder is recursive
/*
Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1)));
Cursor folder = query(db, folderUri, null, null, null, null);
@ -116,63 +121,66 @@ public class FileContentProvider extends ContentProvider {
folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH));
}
*/
Cursor children = query(uri, null, null, null, null);
if (children != null && children.moveToFirst()) {
long childId;
boolean isDir;
while (!children.isAfterLast()) {
childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID));
isDir = "DIR".equals(children.getString(
children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)
));
//remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH));
if (isDir) {
count += delete(
db,
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId),
null,
null
);
} else {
count += delete(
db,
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId),
null,
null
);
Cursor children = query(uri, null, null, null, null);
if (children != null && children.moveToFirst()) {
long childId;
boolean isDir;
while (!children.isAfterLast()) {
childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID));
isDir = "DIR".equals(children.getString(
children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)
));
//remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH));
if (isDir) {
count += delete(
db,
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId),
null,
null
);
} else {
count += delete(
db,
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId),
null,
null
);
}
children.moveToNext();
}
children.moveToNext();
}
children.close();
} /*else {
children.close();
} /*else {
Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName);
}
Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) ");
*/
count += db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ")" : ""), whereArgs);
count += db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ")" : ""), whereArgs);
/* Just for log
if (folder != null) {
folder.close();
}*/
break;
case ROOT_DIRECTORY:
//Log_OC.d(TAG, "Removing ROOT!");
count = db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs);
break;
case SHARES:
count = db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs);
break;
case CAPABILITIES:
count = db.delete(ProviderTableMeta.CAPABILITIES_TABLE_NAME, where, whereArgs);
break;
default:
//Log_OC.e(TAG, "Unknown uri " + uri);
throw new IllegalArgumentException("Unknown uri: " + uri.toString());
break;
case ROOT_DIRECTORY:
//Log_OC.d(TAG, "Removing ROOT!");
count = db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs);
break;
case SHARES:
count = db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs);
break;
case CAPABILITIES:
count = db.delete(ProviderTableMeta.CAPABILITIES_TABLE_NAME, where, whereArgs);
break;
case UPLOADS:
count = db.delete(ProviderTableMeta.UPLOADS_TABLE_NAME, where, whereArgs);
break;
default:
//Log_OC.e(TAG, "Unknown uri " + uri);
throw new IllegalArgumentException("Unknown uri: " + uri.toString());
}
return count;
}
@ -207,68 +215,80 @@ public class FileContentProvider extends ContentProvider {
private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) {
switch (mUriMatcher.match(uri)){
case ROOT_DIRECTORY:
case SINGLE_FILE:
String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
String[] projection = new String[] {
ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH,
ProviderTableMeta.FILE_ACCOUNT_OWNER
};
String where = ProviderTableMeta.FILE_PATH + "=? AND " +
ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
String[] whereArgs = new String[] {remotePath, accountName};
Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
// ugly patch; serious refactorization is needed to reduce work in
// FileDataStorageManager and bring it to FileContentProvider
if (doubleCheck == null || !doubleCheck.moveToFirst()) {
if (doubleCheck != null) {
case ROOT_DIRECTORY:
case SINGLE_FILE:
String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
String[] projection = new String[] {
ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH,
ProviderTableMeta.FILE_ACCOUNT_OWNER
};
String where = ProviderTableMeta.FILE_PATH + "=? AND " +
ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
String[] whereArgs = new String[] {remotePath, accountName};
Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
// ugly patch; serious refactorization is needed to reduce work in
// FileDataStorageManager and bring it to FileContentProvider
if (doubleCheck == null || !doubleCheck.moveToFirst()) {
if (doubleCheck != null) {
doubleCheck.close();
}
long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, null, values);
if (rowId > 0) {
return ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
} else {
throw new SQLException("ERROR " + uri);
}
} else {
// file is already inserted; race condition, let's avoid a duplicated entry
Uri insertedFileUri = ContentUris.withAppendedId(
ProviderTableMeta.CONTENT_URI_FILE,
doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID))
);
doubleCheck.close();
return insertedFileUri;
}
long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, null, values);
if (rowId > 0) {
return ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
case SHARES:
Uri insertedShareUri = null;
long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values);
if (rowId >0) {
insertedShareUri =
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId);
} else {
throw new SQLException("ERROR " + uri);
}
} else {
// file is already inserted; race condition, let's avoid a duplicated entry
Uri insertedFileUri = ContentUris.withAppendedId(
ProviderTableMeta.CONTENT_URI_FILE,
doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID))
);
doubleCheck.close();
updateFilesTableAccordingToShareInsertion(db, values);
return insertedShareUri;
return insertedFileUri;
}
case CAPABILITIES:
Uri insertedCapUri = null;
long id = db.insert(ProviderTableMeta.CAPABILITIES_TABLE_NAME, null, values);
if (id >0) {
insertedCapUri =
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_CAPABILITIES, id);
} else {
throw new SQLException("ERROR " + uri);
case SHARES:
Uri insertedShareUri = null;
long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values);
if (rowId >0) {
insertedShareUri =
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId);
} else {
throw new SQLException("ERROR " + uri);
}
return insertedCapUri;
}
updateFilesTableAccordingToShareInsertion(db, values);
return insertedShareUri;
case UPLOADS:
Uri insertedUploadUri = null;
long uploadId = db.insert(ProviderTableMeta.UPLOADS_TABLE_NAME, null, values);
if (uploadId >0) {
insertedUploadUri =
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_UPLOADS, uploadId);
trimSuccessfulUploads(db);
} else {
throw new SQLException("ERROR " + uri);
case CAPABILITIES:
Uri insertedCapUri = null;
long id = db.insert(ProviderTableMeta.CAPABILITIES_TABLE_NAME, null, values);
if (id >0) {
insertedCapUri =
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_CAPABILITIES, id);
} else {
throw new SQLException("ERROR " + uri);
}
return insertedCapUri;
default:
throw new IllegalArgumentException("Unknown uri id: " + uri);
}
return insertedUploadUri;
default:
throw new IllegalArgumentException("Unknown uri id: " + uri);
}
}
@ -312,6 +332,8 @@ public class FileContentProvider extends ContentProvider {
mUriMatcher.addURI(authority, "shares/#", SHARES);
mUriMatcher.addURI(authority, "capabilities/", CAPABILITIES);
mUriMatcher.addURI(authority, "capabilities/#", CAPABILITIES);
mUriMatcher.addURI(authority, "uploads/", UPLOADS);
mUriMatcher.addURI(authority, "uploads/#", UPLOADS);
return true;
}
@ -352,35 +374,42 @@ public class FileContentProvider extends ContentProvider {
sqlQuery.setTables(ProviderTableMeta.FILE_TABLE_NAME);
switch (mUriMatcher.match(uri)) {
case ROOT_DIRECTORY:
break;
case DIRECTORY:
String folderId = uri.getPathSegments().get(1);
sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
+ folderId);
break;
case SINGLE_FILE:
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
case SHARES:
sqlQuery.setTables(ProviderTableMeta.OCSHARES_TABLE_NAME);
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
case CAPABILITIES:
sqlQuery.setTables(ProviderTableMeta.CAPABILITIES_TABLE_NAME);
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
default:
throw new IllegalArgumentException("Unknown uri id: " + uri);
case ROOT_DIRECTORY:
break;
case DIRECTORY:
String folderId = uri.getPathSegments().get(1);
sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
+ folderId);
break;
case SINGLE_FILE:
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
case SHARES:
sqlQuery.setTables(ProviderTableMeta.OCSHARES_TABLE_NAME);
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
case CAPABILITIES:
sqlQuery.setTables(ProviderTableMeta.CAPABILITIES_TABLE_NAME);
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
case UPLOADS:
sqlQuery.setTables(ProviderTableMeta.UPLOADS_TABLE_NAME);
if (uri.getPathSegments().size() > 1) {
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ uri.getPathSegments().get(1));
}
break;
default:
throw new IllegalArgumentException("Unknown uri id: " + uri);
}
String order;
@ -392,6 +421,9 @@ public class FileContentProvider extends ContentProvider {
case CAPABILITIES:
order = ProviderTableMeta.CAPABILITIES_DEFAULT_SORT_ORDER;
break;
case UPLOADS:
order = ProviderTableMeta.UPLOADS_DEFAULT_SORT_ORDER;
break;
default: // Files
order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
break;
@ -443,6 +475,12 @@ public class FileContentProvider extends ContentProvider {
return db.update(
ProviderTableMeta.CAPABILITIES_TABLE_NAME, values, selection, selectionArgs
);
case UPLOADS:
int ret = db.update(
ProviderTableMeta.UPLOADS_TABLE_NAME, values, selection, selectionArgs
);
trimSuccessfulUploads(db);
return ret;
default:
return db.update(
ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs
@ -485,53 +523,17 @@ public class FileContentProvider extends ContentProvider {
public void onCreate(SQLiteDatabase db) {
// files table
Log_OC.i("SQL", "Entering in onCreate");
db.execSQL("CREATE TABLE " + ProviderTableMeta.FILE_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.FILE_NAME + " TEXT, "
+ ProviderTableMeta.FILE_PATH + " TEXT, "
+ ProviderTableMeta.FILE_PARENT + " INTEGER, "
+ ProviderTableMeta.FILE_CREATION + " INTEGER, "
+ ProviderTableMeta.FILE_MODIFIED + " INTEGER, "
+ ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "
+ ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "
+ ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "
+ ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
+ ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
+ ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
+ ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
+ ProviderTableMeta.FILE_ETAG + " TEXT, "
+ ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER, "
+ ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT, "
+ ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
+ ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
+ ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
+ ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER," //boolean
+ ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT,"
+ ProviderTableMeta.FILE_SHARED_WITH_SHAREE + " INTEGER);"
);
createFilesTable(db);
// Create table ocshares
db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PATH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
+ ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
+ ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
+ ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
// Create ocshares table
createOCSharesTable(db);
// Create table capabilities
// Create capabilities table
createCapabilitiesTable(db);
// Create uploads table
createUploadsTable(db);
}
@Override
@ -619,22 +621,7 @@ public class FileContentProvider extends ContentProvider {
" DEFAULT NULL");
// Create table ocshares
db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PATH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PERMISSIONS + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
+ ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
+ ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
+ ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );");
createOCSharesTable(db);
upgraded = true;
db.setTransactionSuccessful();
@ -760,6 +747,22 @@ public class FileContentProvider extends ContentProvider {
db.endTransaction();
}
}
if (oldVersion < 14 && newVersion >= 14) {
Log_OC.i("SQL", "Entering in the #14 ADD in onUpgrade");
db.beginTransaction();
try {
// drop old instant_upload table
db.execSQL("DROP TABLE IF EXISTS " + "instant_upload" + ";");
// Create uploads table
createUploadsTable(db);
upgraded = true;
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
if (!upgraded)
Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
", newVersion == " + newVersion);
@ -767,8 +770,56 @@ public class FileContentProvider extends ContentProvider {
}
}
private void createFilesTable(SQLiteDatabase db){
db.execSQL("CREATE TABLE " + ProviderTableMeta.FILE_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.FILE_NAME + " TEXT, "
+ ProviderTableMeta.FILE_PATH + " TEXT, "
+ ProviderTableMeta.FILE_PARENT + " INTEGER, "
+ ProviderTableMeta.FILE_CREATION + " INTEGER, "
+ ProviderTableMeta.FILE_MODIFIED + " INTEGER, "
+ ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "
+ ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "
+ ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "
+ ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
+ ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
+ ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
+ ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
+ ProviderTableMeta.FILE_ETAG + " TEXT, "
+ ProviderTableMeta.FILE_SHARED_VIA_LINK + " INTEGER, "
+ ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT, "
+ ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
+ ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
+ ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
+ ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER," //boolean
+ ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT,"
+ ProviderTableMeta.FILE_SHARED_WITH_SHAREE + " INTEGER);"
);
}
private void createOCSharesTable(SQLiteDatabase db) {
// Create ocshares table
db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PATH + " TEXT, "
+ ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
+ ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
+ ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
+ ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
+ ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
+ ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
+ ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
+ ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
}
private void createCapabilitiesTable(SQLiteDatabase db){
// Create table capabilities
// Create capabilities table
db.execSQL("CREATE TABLE " + ProviderTableMeta.CAPABILITIES_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME + " TEXT, "
@ -795,6 +846,35 @@ public class FileContentProvider extends ContentProvider {
+ ProviderTableMeta.CAPABILITIES_FILES_VERSIONING + " INTEGER );" ); // boolean
}
private void createUploadsTable(SQLiteDatabase db){
// Create uploads table
db.execSQL("CREATE TABLE " + ProviderTableMeta.UPLOADS_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.UPLOADS_LOCAL_PATH + " TEXT, "
+ ProviderTableMeta.UPLOADS_REMOTE_PATH + " TEXT, "
+ ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " TEXT, "
+ ProviderTableMeta.UPLOADS_FILE_SIZE + " LONG, "
+ ProviderTableMeta.UPLOADS_STATUS + " INTEGER, " // UploadStatus
+ ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + " INTEGER, " // Upload LocalBehaviour
+ ProviderTableMeta.UPLOADS_UPLOAD_TIME + " INTEGER, "
+ ProviderTableMeta.UPLOADS_FORCE_OVERWRITE + " INTEGER, " // boolean
+ ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + " INTEGER, " // boolean
+ ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + " INTEGER, "
+ ProviderTableMeta.UPLOADS_LAST_RESULT + " INTEGER, " // Upload LastResult
+ ProviderTableMeta.UPLOADS_CREATED_BY + " INTEGER );" // Upload createdBy
);
/* before:
// PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a
// bug in some early versions, this is not the case in SQLite.
//db.execSQL("CREATE TABLE " + TABLE_UPLOAD + " (" + " path TEXT PRIMARY KEY NOT NULL UNIQUE,"
// + " uploadStatus INTEGER NOT NULL, uploadObject TEXT NOT NULL);");
// uploadStatus is used to easy filtering, it has precedence over
// uploadObject.getUploadStatus()
*/
}
/**
* Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names
* structure to include in it the path to the server instance. Updating the account names and path to local files
@ -805,7 +885,7 @@ public class FileContentProvider extends ContentProvider {
* @param db Database where table of files is included.
*/
private void updateAccountName(SQLiteDatabase db){
Log_OC.d("SQL", "THREAD: "+ Thread.currentThread().getName());
Log_OC.d("SQL", "THREAD: " + Thread.currentThread().getName());
AccountManager ama = AccountManager.get(getContext());
try {
// get accounts from AccountManager ; we can't be sure if accounts in it are updated or not although
@ -866,10 +946,10 @@ public class FileContentProvider extends ContentProvider {
ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL";
Cursor c = db.query(ProviderTableMeta.FILE_TABLE_NAME,
null,
whereClause,
new String[] { newAccountName },
null, null, null);
null,
whereClause,
new String[]{newAccountName},
null, null, null);
try {
if (c.moveToFirst()) {
@ -909,4 +989,43 @@ public class FileContentProvider extends ContentProvider {
}
/**
* Grants that total count of successful uploads stored is not greater than MAX_SUCCESSFUL_UPLOADS.
*
* Removes older uploads if needed.
*/
private void trimSuccessfulUploads(SQLiteDatabase db) {
Cursor c = null;
try {
c = db.rawQuery(
"delete from " + ProviderTableMeta.UPLOADS_TABLE_NAME +
" where " + ProviderTableMeta.UPLOADS_STATUS + " == "
+ UploadsStorageManager.UploadStatus.UPLOAD_SUCCEEDED.getValue() +
" and " + ProviderTableMeta._ID +
" not in (select " + ProviderTableMeta._ID +
" from " + ProviderTableMeta.UPLOADS_TABLE_NAME +
" where " + ProviderTableMeta.UPLOADS_STATUS + " == "
+ UploadsStorageManager.UploadStatus.UPLOAD_SUCCEEDED.getValue() +
" order by " + ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP +
" desc limit " + MAX_SUCCESSFUL_UPLOADS +
")",
null
);
c.moveToFirst(); // do something with the cursor, or deletion doesn't happen; true story
} catch (Exception e) {
Log_OC.e(
TAG,
"Something wrong trimming successful uploads, database could grow more than expected",
e
);
} finally {
if (c!= null) {
c.close();
}
}
}
}

View file

@ -52,6 +52,7 @@ import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;
import com.owncloud.android.operations.CheckCurrentCredentialsOperation;
import com.owncloud.android.operations.CopyFileOperation;
import com.owncloud.android.operations.CreateFolderOperation;
import com.owncloud.android.operations.CreateShareViaLinkOperation;
@ -114,6 +115,7 @@ public class OperationsService extends Service {
public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";
public static final String ACTION_MOVE_FILE = "MOVE_FILE";
public static final String ACTION_COPY_FILE = "COPY_FILE";
public static final String ACTION_CHECK_CURRENT_CREDENTIALS = "CHECK_CURRENT_CREDENTIALS";
public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() +
".OPERATION_ADDED";
@ -693,6 +695,10 @@ public class OperationsService extends Service {
String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
String newParentPath = operationIntent.getStringExtra(EXTRA_NEW_PARENT_PATH);
operation = new CopyFileOperation(remotePath, newParentPath, account);
} else if (action.equals(ACTION_CHECK_CURRENT_CREDENTIALS)) {
// Check validity of currently stored credentials for a given account
operation = new CheckCurrentCredentialsOperation(account);
}
}

View file

@ -1,7 +1,7 @@
/**
* ownCloud Android client application
*
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -27,7 +27,6 @@ import android.os.Looper;
import android.os.Message;
import android.util.Pair;
import com.owncloud.android.MainApp;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader;
@ -82,7 +81,7 @@ class SyncFolderHandler extends Handler {
*/
public boolean isSynchronizing(Account account, String remotePath) {
if (account == null || remotePath == null) return false;
return (mPendingOperations.contains(account, remotePath));
return (mPendingOperations.contains(account.name, remotePath));
}
@ -100,7 +99,7 @@ class SyncFolderHandler extends Handler {
*/
private void doOperation(Account account, String remotePath) {
mCurrentSyncOperation = mPendingOperations.get(account, remotePath);
mCurrentSyncOperation = mPendingOperations.get(account.name, remotePath);
if (mCurrentSyncOperation != null) {
RemoteOperationResult result = null;
@ -127,7 +126,7 @@ class SyncFolderHandler extends Handler {
} catch (IOException e) {
Log_OC.e(TAG, "Error while trying to get authorization", e);
} finally {
mPendingOperations.removePayload(account, remotePath);
mPendingOperations.removePayload(account.name, remotePath);
mService.dispatchResultToOperationListeners(mCurrentSyncOperation, result);
@ -139,7 +138,7 @@ class SyncFolderHandler extends Handler {
public void add(Account account, String remotePath,
SynchronizeFolderOperation syncFolderOperation){
Pair<String, String> putResult =
mPendingOperations.putIfAbsent(account, remotePath, syncFolderOperation);
mPendingOperations.putIfAbsent(account.name, remotePath, syncFolderOperation);
if (putResult != null) {
sendBroadcastNewSyncFolder(account, remotePath); // TODO upgrade!
}
@ -149,7 +148,7 @@ class SyncFolderHandler extends Handler {
/**
* Cancels a pending or current sync' operation.
*
* @param account ownCloud account where the remote file is stored.
* @param account ownCloud {@link Account} where the remote file is stored.
* @param file A file in the queue of pending synchronizations
*/
public void cancel(Account account, OCFile file){
@ -158,7 +157,7 @@ class SyncFolderHandler extends Handler {
return;
}
Pair<SynchronizeFolderOperation, String> removeResult =
mPendingOperations.remove(account, file.getRemotePath());
mPendingOperations.remove(account.name, file.getRemotePath());
SynchronizeFolderOperation synchronization = removeResult.first;
if (synchronization != null) {
synchronization.cancel();

View file

@ -3,7 +3,7 @@
*
* @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,

View file

@ -300,9 +300,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
} else if (result.getCode() != ResultCode.FILE_NOT_FOUND) {
// in failures, the statistics for the global result are updated
if ( result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
result.isIdPRedirection()
) {
if (RemoteOperationResult.ResultCode.UNAUTHORIZED.equals(result.getCode())) {
mSyncResult.stats.numAuthExceptions++;
} else if (result.getException() instanceof DavException) {
@ -395,10 +393,8 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
private void notifyFailedSynchronization() {
NotificationCompat.Builder notificationBuilder = createNotificationBuilder();
boolean needsToUpdateCredentials = (
mLastFailedResult != null && (
mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
mLastFailedResult.isIdPRedirection()
)
mLastFailedResult != null &&
ResultCode.UNAUTHORIZED.equals(mLastFailedResult.getCode())
);
if (needsToUpdateCredentials) {
// let the user update credentials with one click

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -29,19 +29,22 @@ import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
public interface ComponentsGetter {
/**
* To be invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
* To be invoked when the parent activity is fully created to get a reference
* to the FileDownloader service API.
*/
public FileDownloaderBinder getFileDownloaderBinder();
/**
* To be invoked when the parent activity is fully created to get a reference to the FileUploader service API.
* To be invoked when the parent activity is fully created to get a reference
* to the FileUploader service API.
*/
public FileUploaderBinder getFileUploaderBinder();
/**
* To be invoked when the parent activity is fully created to get a reference to the OperationsSerivce service API.
* To be invoked when the parent activity is fully created to get a reference
* to the OperationsSerivce service API.
*/
public OperationsServiceBinder getOperationsServiceBinder();

View file

@ -1,23 +1,22 @@
/**
* ownCloud Android client application
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2016 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/>.
* 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.
* <p/>
* 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.ui.activity;
@ -33,6 +32,7 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
/**
* Wrapper activity which will be launched if keep-in-sync file will be modified by external
* application.
@ -40,7 +40,7 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionM
public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
private String TAG = ConflictsResolveActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -48,18 +48,20 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
@Override
public void conflictDecisionMade(Decision decision) {
Intent i = new Intent(getApplicationContext(), FileUploader.class);
Integer behaviour = null;
Boolean forceOverwrite = null;
switch (decision) {
case CANCEL:
finish();
return;
case OVERWRITE:
// use local version -> overwrite on server
i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
forceOverwrite = true;
break;
case KEEP_BOTH:
i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
behaviour = FileUploader.LOCAL_BEHAVIOUR_MOVE;
break;
case SERVER:
// use server version -> delete local, request download
@ -73,11 +75,9 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
return;
}
i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
i.putExtra(FileUploader.KEY_FILE, getFile());
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
startService(i);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadUpdate(this, getAccount(), getFile(), behaviour, forceOverwrite);
finish();
}
@ -91,21 +91,22 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
finish();
} else {
/// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
file = getStorageManager().getFileByPath(file.getRemotePath()); // file = null if not in the current Account
file = getStorageManager().getFileByPath(file.getRemotePath()); // file = null if not in the
// current Account
if (file != null) {
setFile(file);
ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
d.showDialog(this);
} else {
// account was changed to a different one - just finish
finish();
}
}
} else {
finish();
}
}
}

View file

@ -3,7 +3,7 @@
*
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -111,8 +111,10 @@ public class FileActivity extends AppCompatActivity
private static final String DIALOG_SHARE_PASSWORD = "DIALOG_SHARE_PASSWORD";
private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE";
protected static final long DELAY_TO_REQUEST_OPERATIONS_LATER = 200;
public static final int REQUEST_CODE__UPDATE_CREDENTIALS = 0;
public static final int REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS;
protected static final long DELAY_TO_REQUEST_OPERATIONS_LATER = 200;
/** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/
private Account mAccount;
@ -278,7 +280,6 @@ public class FileActivity extends AppCompatActivity
super.onPause();
}
@Override
protected void onDestroy() {
if (mOperationsServiceConnection != null) {
@ -400,13 +401,18 @@ public class FileActivity extends AppCompatActivity
//mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2],
// mDrawerContentDescriptions[2]));
// Uploads
mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[2],
R.drawable.ic_uploads));
// Settings
mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[1],
mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], mDrawerContentDescriptions[1],
R.drawable.ic_settings));
// Logs
if (BuildConfig.DEBUG) {
mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2],
mDrawerContentDescriptions[2],R.drawable.ic_log));
mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[3],
mDrawerContentDescriptions[3], R.drawable.ic_log));
}
// setting the nav drawer list adapter
@ -417,6 +423,7 @@ public class FileActivity extends AppCompatActivity
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.string.drawer_open,R.string.drawer_close) {
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
@ -448,7 +455,7 @@ public class FileActivity extends AppCompatActivity
* @param navigationDrawerLayout the drawer layout to be used
* @param account the account to be set in the drawer
*/
protected void setUsernameInDrawer(RelativeLayout navigationDrawerLayout, Account account) {
protected void setUsernameInDrawer(View navigationDrawerLayout, Account account) {
if (navigationDrawerLayout != null && account != null) {
TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
int lastAtPos = account.name.lastIndexOf("@");
@ -462,7 +469,7 @@ public class FileActivity extends AppCompatActivity
* Assumes that navigation drawer is NOT visible.
*/
protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
String title = getString(R.string.default_display_name_for_root_folder); // default
String title = getDefaultTitle(); // default
boolean inRoot;
/// choose the appropiate title
@ -495,6 +502,9 @@ public class FileActivity extends AppCompatActivity
}
protected String getDefaultTitle() {
return getString(R.string.default_display_name_for_root_folder);
}
/**
* Sets and validates the ownCloud {@link Account} associated to the Activity.
@ -725,7 +735,7 @@ public class FileActivity extends AppCompatActivity
/**
*
* @param operation Removal operation performed.
* @param operation Operation performed.
* @param result Result of the removal.
*/
@Override
@ -739,14 +749,12 @@ public class FileActivity extends AppCompatActivity
if (!result.isSuccess() && (
result.getCode() == ResultCode.UNAUTHORIZED ||
result.isIdPRedirection() ||
(result.isException() && result.getException() instanceof AuthenticatorException)
)) {
requestCredentialsUpdate(this);
if (result.getCode() == ResultCode.UNAUTHORIZED) {
dismissLoadingDialog();
Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
operation, getResources()),
Toast.LENGTH_LONG);
@ -793,16 +801,34 @@ public class FileActivity extends AppCompatActivity
* Invalidates the credentials stored for the current OC account and requests new credentials to the user,
* navigating to {@link AuthenticatorActivity}
*
* Equivalent to call requestCredentialsUpdate(context, null);
*
* @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
* to make the method accessible to {@link android.content.BroadcastReceiver}s.
*/
protected void requestCredentialsUpdate(Context context) {
requestCredentialsUpdate(context, null);
}
/**
* Invalidates the credentials stored for the given OC account and requests new credentials to the user,
* navigating to {@link AuthenticatorActivity}
*
* @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
* to make the method accessible to {@link android.content.BroadcastReceiver}s.
* @param account Stored OC account to request credentials update for. If null, current account will
* be used.
*/
protected void requestCredentialsUpdate(Context context, Account account) {
try {
/// step 1 - invalidate credentials of current account
if (account == null) {
account = getAccount();
}
OwnCloudClient client;
OwnCloudAccount ocAccount =
new OwnCloudAccount(getAccount(), context);
new OwnCloudAccount(account, context);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
if (client != null) {
@ -811,23 +837,23 @@ public class FileActivity extends AppCompatActivity
AccountManager am = AccountManager.get(context);
if (cred.authTokenExpires()) {
am.invalidateAuthToken(
getAccount().type,
account.type,
cred.getAuthToken()
);
} else {
am.clearPassword(getAccount());
am.clearPassword(account);
}
}
}
/// step 2 - request credentials to user
Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
updateAccountCredentials.putExtra(
AuthenticatorActivity.EXTRA_ACTION,
AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(updateAccountCredentials);
startActivityForResult(updateAccountCredentials, REQUEST_CODE__UPDATE_CREDENTIALS);
} catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
@ -997,11 +1023,6 @@ public class FileActivity extends AppCompatActivity
startActivity(i);
}
// TODO re-enable when "Accounts" is available in Navigation Drawer
// public void closeDrawer() {
// mDrawerLayout.closeDrawers();
// }
public void allFilesOption(){
restart();
}
@ -1023,29 +1044,34 @@ public class FileActivity extends AppCompatActivity
case 0: // All Files
allFilesOption();
mDrawerLayout.closeDrawers();
break;
// TODO Enable when "On Device" is recovered ?
// case 2:
// MainApp.showOnlyFilesOnDevice(true);
// mDrawerLayout.closeDrawers();
// break;
case 1: // Settings
case 1: // Uploads
Intent uploadListIntent = new Intent(getApplicationContext(),
UploadListActivity.class);
uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(uploadListIntent);
break;
case 2: // Settings
Intent settingsIntent = new Intent(getApplicationContext(),
Preferences.class);
startActivity(settingsIntent);
mDrawerLayout.closeDrawers();
break;
case 2: // Logs
case 3: // Logs
Intent loggerIntent = new Intent(getApplicationContext(),
LogHistoryActivity.class);
startActivity(loggerIntent);
mDrawerLayout.closeDrawers();
break;
}
mDrawerLayout.closeDrawers();
}
}
}

View file

@ -1,23 +1,22 @@
/**
* ownCloud Android client application
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2016 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 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/>.
* 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.ui.activity;
@ -57,7 +56,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.owncloud.android.MainApp;
@ -80,10 +78,10 @@ import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.services.observer.FileObserverService;
import com.owncloud.android.syncadapter.FileSyncAdapter;
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.fragment.FileDetailFragment;
@ -103,11 +101,11 @@ import com.owncloud.android.utils.UriUtils;
import java.io.File;
/**
* Displays, what files the user has available in his ownCloud.
* Displays, what files the user has available in his ownCloud. This is the main view.
*/
public class FileDisplayActivity extends HookActivity
implements FileFragment.ContainerActivity,
public class FileDisplayActivity extends HookActivity implements
FileFragment.ContainerActivity,
OnSslUntrustedCertListener, OnEnforceableRefreshListener {
private SyncBroadcastReceiver mSyncBroadcastReceiver;
@ -126,10 +124,10 @@ public class FileDisplayActivity extends HookActivity
public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
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;
public static final int ACTION_COPY_FILES = 4;
public static final int REQUEST_CODE__SELECT_CONTENT_FROM_APPS = REQUEST_CODE__LAST_SHARED + 1;
public static final int REQUEST_CODE__SELECT_MULTIPLE_FILES = REQUEST_CODE__LAST_SHARED + 2;
public static final int REQUEST_CODE__MOVE_FILES = REQUEST_CODE__LAST_SHARED + 3;
public static final int REQUEST_CODE__COPY_FILES = REQUEST_CODE__LAST_SHARED + 4;
private static final String TAG = FileDisplayActivity.class.getSimpleName();
@ -152,7 +150,7 @@ public class FileDisplayActivity extends HookActivity
Log_OC.v(TAG, "onCreate() start");
super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account
// is valid
// is valid
/// grant that FileObserverService is watching favorite files
if (savedInstanceState == null) {
@ -161,7 +159,7 @@ public class FileDisplayActivity extends HookActivity
}
/// Load of saved instance state
if(savedInstanceState != null) {
if (savedInstanceState != null) {
mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(
FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
@ -192,8 +190,8 @@ public class FileDisplayActivity extends HookActivity
// Action bar setup
getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS,
// according to the official
// documentation
// according to the official
// documentation
// enable ActionBar app icon to behave as action to toggle nav drawer
//getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -297,7 +295,7 @@ public class FileDisplayActivity extends HookActivity
// cache until the upload is successful get parent from path
parentPath = file.getRemotePath().substring(0,
file.getRemotePath().lastIndexOf(file.getFileName()));
if (getStorageManager().getFileByPath(parentPath) == null)
if (getStorageManager().getFileByPath(parentPath) == null)
file = null; // not able to know the directory where the file is uploading
} else {
file = getStorageManager().getFileByPath(file.getRemotePath());
@ -311,7 +309,7 @@ public class FileDisplayActivity extends HookActivity
setFile(file);
if (mAccountWasSet) {
setUsernameInDrawer((RelativeLayout) findViewById(R.id.left_drawer), getAccount());
setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount());
}
if (!stateWasRecovered) {
@ -355,7 +353,6 @@ public class FileDisplayActivity extends HookActivity
setSecondFragment(secondFragment);
updateFragmentsVisibility(true);
updateActionBarTitleAndHomeButton(file);
} else {
cleanSecondFragment();
if (file.isDown() && PreviewTextFragment.canBePreviewed(file))
@ -389,8 +386,8 @@ public class FileDisplayActivity extends HookActivity
} else if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
secondFragment = null;
} else {
secondFragment = FileDetailFragment.newInstance(file, getAccount());
}
secondFragment = FileDetailFragment.newInstance(file, getAccount());
}
}
return secondFragment;
}
@ -488,7 +485,7 @@ public class FileDisplayActivity extends HookActivity
OCFile fileInFragment = detailsFragment.getFile();
if (fileInFragment != null &&
!downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
// the user browsed to other file ; forget the automatic preview
// the user browsed to other file ; forget the automatic preview
mWaitingToPreview = null;
} else if (downloadEvent.equals(FileDownloader.getDownloadAddedMessage())) {
@ -503,7 +500,7 @@ public class FileDisplayActivity extends HookActivity
if (success) {
mWaitingToPreview = getStorageManager().getFileById(
mWaitingToPreview.getFileId()); // update the file from database,
// for the local storage path
// for the local storage path
if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
startMediaPreview(mWaitingToPreview, 0, true);
detailsFragmentChanged = true;
@ -540,7 +537,7 @@ public class FileDisplayActivity extends HookActivity
menu.findItem(R.id.action_create_dir).setVisible(false);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@ -555,7 +552,7 @@ public class FileDisplayActivity extends HookActivity
OCFile currentDir = getCurrentDir();
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else if((currentDir != null && currentDir.getParentId() != 0) ||
} else if ((currentDir != null && currentDir.getParentId() != 0) ||
(second != null && second.getFile() != null)) {
onBackPressed();
@ -574,26 +571,26 @@ public class FileDisplayActivity extends HookActivity
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.actionbar_sort_title)
.setSingleChoiceItems(R.array.actionbar_sortby, sortOrder ,
.setSingleChoiceItems(R.array.actionbar_sortby, sortOrder,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switch (which){
case 0:
sortByName(true);
break;
case 1:
sortByDate(false);
break;
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
sortByName(true);
break;
case 1:
sortByDate(false);
break;
}
dialog.dismiss();
}
});
dialog.dismiss();
}
});
builder.create().show();
break;
}
case R.id.action_switch_view:{
if (isGridView()){
case R.id.action_switch_view: {
if (isGridView()) {
item.setTitle(getString(R.string.action_switch_grid_view));
item.setIcon(ContextCompat.getDrawable(getApplicationContext(),
R.drawable.ic_view_module));
@ -606,8 +603,8 @@ public class FileDisplayActivity extends HookActivity
}
return true;
}
default:
retval = super.onOptionsItemSelected(item);
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
@ -646,13 +643,12 @@ public class FileDisplayActivity extends HookActivity
/**
* Called, when the user selected something for uploading
*
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK ||
if (requestCode == REQUEST_CODE__SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK ||
resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
//getClipData is only supported on api level 16+, Jelly Bean
@ -660,7 +656,7 @@ public class FileDisplayActivity extends HookActivity
data.getClipData() != null &&
data.getClipData().getItemCount() > 0) {
for( int i = 0; i < data.getClipData().getItemCount(); i++){
for (int i = 0; i < data.getClipData().getItemCount(); i++) {
Intent intent = new Intent();
intent.setData(data.getClipData().getItemAt(i).getUri());
requestSimpleUpload(intent, resultCode);
@ -669,11 +665,11 @@ public class FileDisplayActivity extends HookActivity
} else {
requestSimpleUpload(data, resultCode);
}
} else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK ||
} else if (requestCode == REQUEST_CODE__SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK ||
resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
requestMultipleUpload(data, resultCode);
} else if (requestCode == ACTION_MOVE_FILES && resultCode == RESULT_OK){
} else if (requestCode == REQUEST_CODE__MOVE_FILES && resultCode == RESULT_OK) {
final Intent fData = data;
final int fResultCode = resultCode;
getHandler().postDelayed(
@ -686,7 +682,7 @@ public class FileDisplayActivity extends HookActivity
DELAY_TO_REQUEST_OPERATIONS_LATER
);
} else if (requestCode == ACTION_COPY_FILES && resultCode == RESULT_OK) {
} else if (requestCode == REQUEST_CODE__COPY_FILES && resultCode == RESULT_OK) {
final Intent fData = data;
final int fResultCode = resultCode;
@ -711,18 +707,23 @@ public class FileDisplayActivity extends HookActivity
if (filePaths != null) {
String[] remotePaths = new String[filePaths.length];
String remotePathBase = getCurrentDir().getRemotePath();
for (int j = 0; j< remotePaths.length; j++) {
for (int j = 0; j < remotePaths.length; j++) {
remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
}
Intent i = new Intent(this, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
startService(i);
int behaviour = (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE) ? FileUploader
.LOCAL_BEHAVIOUR_MOVE : FileUploader.LOCAL_BEHAVIOUR_COPY;
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
this,
getAccount(),
filePaths,
remotePaths,
null, // MIME type will be detected from file name
behaviour,
false, // do not create parent folder if not existent
UploadFileOperation.CREATED_BY_USER
);
} else {
Log_OC.d(TAG, "User clicked on 'Update' with no selection");
@ -768,9 +769,10 @@ public class FileDisplayActivity extends HookActivity
}
Intent i = new Intent(this, FileUploader.class);
i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
i.putExtra(FileUploader.KEY_ACCOUNT,
getAccount());
OCFile currentDir = getCurrentDir();
String remotePath = (currentDir != null) ? currentDir.getRemotePath() : OCFile.ROOT_PATH;
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);
@ -778,7 +780,7 @@ public class FileDisplayActivity extends HookActivity
if (cursor != null && cursor.moveToFirst()) {
String displayName = cursor.getString(cursor.getColumnIndex(
OpenableColumns.DISPLAY_NAME));
Log_OC.v(TAG, "Display Name: " + displayName );
Log_OC.v(TAG, "Display Name: " + displayName);
displayName.replace(File.separatorChar, '_');
displayName.replace(File.pathSeparatorChar, '_');
@ -794,13 +796,20 @@ public class FileDisplayActivity extends HookActivity
remotePath += new File(filePath).getName();
}
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);
startService(i);
int behaviour = (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE) ? FileUploader.LOCAL_BEHAVIOUR_MOVE :
FileUploader.LOCAL_BEHAVIOUR_COPY;
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
this,
getAccount(),
filePath,
remotePath,
behaviour,
mimeType,
false, // do not create parent folder if not existent
UploadFileOperation.CREATED_BY_USER
);
}
/**
@ -882,7 +891,6 @@ public class FileDisplayActivity extends HookActivity
Log_OC.v(TAG, "onSaveInstanceState() end");
}
@Override
protected void onResume() {
Log_OC.v(TAG, "onResume() start");
@ -964,6 +972,7 @@ public class FileDisplayActivity extends HookActivity
String event = intent.getAction();
Log_OC.d(TAG, "Received broadcast " + event);
String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
String synchFolderRemotePath =
intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
RemoteOperationResult synchResult =
@ -982,16 +991,17 @@ public class FileDisplayActivity extends HookActivity
getStorageManager().getFileByPath(getFile().getRemotePath());
OCFile currentDir = (getCurrentDir() == null) ? null :
getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
if (currentDir == null) {
// current folder was removed from the server
Toast.makeText( FileDisplayActivity.this,
String.format(
getString(R.string.
sync_current_folder_was_removed),
synchFolderRemotePath),
Toast.LENGTH_LONG)
.show();
Toast.makeText(FileDisplayActivity.this,
String.format(
getString(R.string.
sync_current_folder_was_removed),
synchFolderRemotePath),
Toast.LENGTH_LONG)
.show();
browseToRoot();
@ -1019,13 +1029,12 @@ public class FileDisplayActivity extends HookActivity
mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) &&
!RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED
.equals(event));
if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
equals(event) &&/// TODO refactor and make common
equals(event) &&/// TODO refactor and make common
synchResult != null && !synchResult.isSuccess() &&
(synchResult.getCode() == ResultCode.UNAUTHORIZED ||
synchResult.isIdPRedirection() ||
(ResultCode.UNAUTHORIZED.equals(synchResult.getCode()) ||
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
@ -1049,7 +1058,7 @@ public class FileDisplayActivity extends HookActivity
}
}
} catch (RuntimeException e) {
// avoid app crashes after changing the serial id of RemoteOperationResult
// avoid app crashes after changing the serial id of RemoteOperationResult
// in owncloud library with broadcast notifications pending to process
removeStickyBroadcast(intent);
}
@ -1102,10 +1111,12 @@ public class FileDisplayActivity extends HookActivity
}
}
boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT,
boolean uploadWasFine = intent.getBooleanExtra(
FileUploader.EXTRA_UPLOAD_RESULT,
false);
boolean renamedInUpload = getFile().getRemotePath().
equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
boolean sameFile = getFile().getRemotePath().equals(uploadedRemotePath) ||
renamedInUpload;
FileFragment details = getSecondFragment();
@ -1115,6 +1126,8 @@ public class FileDisplayActivity extends HookActivity
if (sameAccount && sameFile && detailFragmentIsShown) {
if (uploadWasFine) {
setFile(getStorageManager().getFileByPath(uploadedRemotePath));
} else {
//TODO remove upload progress bar after upload failed.
}
if (renamedInUpload) {
String newName = (new File(uploadedRemotePath)).getName();
@ -1144,6 +1157,7 @@ public class FileDisplayActivity extends HookActivity
}
mProgressBar.setIndeterminate(false);
} finally {
if (intent != null) {
removeStickyBroadcast(intent);
@ -1161,13 +1175,12 @@ public class FileDisplayActivity extends HookActivity
);
}
}
/**
* Class waiting for broadcast events from the {@link FileDownloader} service.
*
* <p/>
* Updates the UI when a download is started or finished, provided that it is relevant for the
* current folder.
*/
@ -1212,17 +1225,17 @@ public class FileDisplayActivity extends HookActivity
private boolean isDescendant(String downloadedRemotePath) {
OCFile currentDir = getCurrentDir();
return (
currentDir != null &&
downloadedRemotePath != null &&
downloadedRemotePath.startsWith(currentDir.getRemotePath())
currentDir != null &&
downloadedRemotePath != null &&
downloadedRemotePath.startsWith(currentDir.getRemotePath())
);
}
private boolean isAscendant(String linkedToRemotePath) {
OCFile currentDir = getCurrentDir();
return (
currentDir != null &&
currentDir.getRemotePath().startsWith(linkedToRemotePath)
currentDir != null &&
currentDir.getRemotePath().startsWith(linkedToRemotePath)
);
}
@ -1250,7 +1263,6 @@ public class FileDisplayActivity extends HookActivity
/**
* {@inheritDoc}
* <p/>
* Updates action bar and second fragment, if in dual pane mode.
*/
@Override
@ -1306,7 +1318,7 @@ public class FileDisplayActivity extends HookActivity
mDownloaderBinder = (FileDownloaderBinder) service;
if (mWaitingToPreview != null)
if (getStorageManager() != null) {
// update the file
// update the file
mWaitingToPreview =
getStorageManager().getFileById(mWaitingToPreview.getFileId());
if (!mWaitingToPreview.isDown()) {
@ -1425,9 +1437,9 @@ public class FileDisplayActivity extends HookActivity
/**
* Updates the view associated to the activity after the finish of an operation trying to
* remove a file.
*
* @param operation Removal operation performed.
* @param result Result of the removal.
*
* @param operation Removal operation performed.
* @param result Result of the removal.
*/
private void onRemoveFileOperationFinish(RemoveFileOperation operation,
RemoteOperationResult result) {
@ -1446,7 +1458,7 @@ public class FileDisplayActivity extends HookActivity
setFile(getStorageManager().getFileById(removedFile.getParentId()));
cleanSecondFragment();
}
if (getStorageManager().getFileById(removedFile.getParentId()).equals(getCurrentDir())){
if (getStorageManager().getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
refreshListOfFilesFragment();
}
invalidateOptionsMenu();
@ -1509,9 +1521,9 @@ public class FileDisplayActivity extends HookActivity
/**
* Updates the view associated to the activity after the finish of an operation trying to rename
* a file.
*
* @param operation Renaming operation performed.
* @param result Result of the renaming.
*
* @param operation Renaming operation performed.
* @param result Result of the renaming.
*/
private void onRenameFileOperationFinish(RenameFileOperation operation,
RemoteOperationResult result) {
@ -1520,7 +1532,7 @@ public class FileDisplayActivity extends HookActivity
FileFragment details = getSecondFragment();
if (details != null) {
if (details instanceof FileDetailFragment &&
renamedFile.equals(details.getFile()) ) {
renamedFile.equals(details.getFile())) {
((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
showDetails(renamedFile);
@ -1544,7 +1556,7 @@ public class FileDisplayActivity extends HookActivity
}
}
if (getStorageManager().getFileById(renamedFile.getParentId()).equals(getCurrentDir())){
if (getStorageManager().getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
refreshListOfFilesFragment();
}
@ -1561,6 +1573,7 @@ public class FileDisplayActivity extends HookActivity
}
}
private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
RemoteOperationResult result) {
if (result.isSuccess()) {
@ -1576,9 +1589,9 @@ public class FileDisplayActivity extends HookActivity
/**
* 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.
*
* @param operation Creation operation performed.
* @param result Result of the creation.
*/
private void onCreateFolderOperationFinish(CreateFolderOperation operation,
RemoteOperationResult result) {
@ -1606,7 +1619,7 @@ public class FileDisplayActivity extends HookActivity
refreshListOfFilesFragment();
FileFragment details = getSecondFragment();
if (details != null && details instanceof FileDetailFragment &&
file.equals(details.getFile()) ) {
file.equals(details.getFile())) {
if (downloading || uploading) {
((FileDetailFragment) details).updateFileDetails(file, getAccount());
} else {
@ -1649,16 +1662,16 @@ public class FileDisplayActivity extends HookActivity
/**
* Starts an operation to refresh the requested folder.
*
* <p/>
* The operation is run in a new background thread created on the fly.
*
* <p/>
* The refresh updates is a "light sync": properties of regular files in folder are updated (including
* associated shares), but not their contents. Only the contents of files marked to be kept-in-sync are
* synchronized too.
*
* @param folder Folder to refresh.
* @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag
* didn't change.
* @param folder Folder to refresh.
* @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag
* didn't change.
*/
public void startSyncFolderOperation(final OCFile folder, final boolean ignoreETag) {
@ -1755,16 +1768,17 @@ public class FileDisplayActivity extends HookActivity
showDetailsIntent.putExtra(EXTRA_FILE, file);
showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
startActivity(showDetailsIntent);
}
/**
* Stars the preview of an already down media {@link OCFile}.
*
* @param file Media {@link OCFile} to preview.
* @param startPlaybackPosition Media position where the playback will be started,
* in milliseconds.
* @param autoplay When 'true', the playback will start without user
* interactions.
* @param file Media {@link OCFile} to preview.
* @param startPlaybackPosition Media position where the playback will be started,
* in milliseconds.
* @param autoplay When 'true', the playback will start without user
* interactions.
*/
public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition,
@ -1856,9 +1870,12 @@ public class FileDisplayActivity extends HookActivity
private void sortByName(boolean ascending) {
getListOfFilesFragment().sortByName(ascending);
}
private boolean isGridView(){ return getListOfFilesFragment().isGridView(); }
public void allFilesOption() {
browseToRoot();
}
private boolean isGridView() {
return getListOfFilesFragment().isGridView();
}
public void allFilesOption() {
browseToRoot();
}
}

View file

@ -1,7 +1,7 @@
/**
* ownCloud Android client application
*
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -20,7 +20,6 @@
package com.owncloud.android.ui.activity;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -44,11 +43,6 @@ import android.widget.Toast;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudCredentials;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
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;
@ -379,6 +373,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
data.putExtra(EXTRA_FILE, targetFile);
}
setResult(RESULT_OK, data);
finish();
}
}
@ -486,9 +481,8 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
equals(event) &&
/// TODO refactor and make common
synchResult != null && !synchResult.isSuccess() &&
(synchResult.getCode() == ResultCode.UNAUTHORIZED ||
synchResult.isIdPRedirection() ||
(synchResult.isException() && synchResult.getException()
(ResultCode.UNAUTHORIZED.equals(synchResult.getCode()) ||
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
requestCredentialsUpdate(context);

View file

@ -4,7 +4,7 @@
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -71,7 +71,6 @@ 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;
@ -92,12 +91,12 @@ public class Preferences extends PreferenceActivity
private static final String TAG = Preferences.class.getSimpleName();
private static final int ACTION_SELECT_UPLOAD_PATH = 1;
private static final int ACTION_SELECT_UPLOAD_VIDEO_PATH = 2;
private static final int ACTION_REQUEST_PASSCODE = 5;
private static final int ACTION_CONFIRM_PASSCODE = 6;
private DbHandler mDbHandler;
private CheckBoxPreference pCode;
private Preference pAboutApp;
private AppCompatDelegate mDelegate;
@ -128,7 +127,6 @@ public class Preferences extends PreferenceActivity
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
mDbHandler = new DbHandler(getBaseContext());
addPreferencesFromResource(R.xml.preferences);
ActionBar actionBar = getSupportActionBar();
@ -307,7 +305,8 @@ public class Preferences extends PreferenceActivity
String username = currentAccount.name.substring(0,
currentAccount.name.lastIndexOf('@'));
String recommendSubject = String.format(getString(R.string.recommend_subject),
String recommendSubject =
String.format(getString(R.string.recommend_subject),
appName);
String recommendText = String.format(getString(R.string.recommend_text),
appName, downloadUrl);
@ -334,7 +333,8 @@ public class Preferences extends PreferenceActivity
@Override
public boolean onPreferenceClick(Preference preference) {
String feedbackMail =(String) getText(R.string.mail_feedback);
String feedback =(String) getText(R.string.prefs_feedback) + " - android v" + appVersion;
String feedback =(String) getText(R.string.prefs_feedback) +
" - android v" + appVersion;
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
@ -453,7 +453,8 @@ public class Preferences extends PreferenceActivity
/* About App */
pAboutApp = (Preference) findPreference("about_app");
if (pAboutApp != null) {
pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
pAboutApp.setTitle(String.format(getString(R.string.about_android),
getString(R.string.app_name)));
pAboutApp.setSummary(String.format(getString(R.string.about_version), appVersion));
}
@ -692,8 +693,6 @@ public class Preferences extends PreferenceActivity
@Override
protected void onDestroy() {
mDbHandler.close();
if (mDownloadServiceConnection != null) {
unbindService(mDownloadServiceConnection);
mDownloadServiceConnection = null;
@ -912,7 +911,8 @@ public class Preferences extends PreferenceActivity
if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
} else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
} else if (component.equals(new ComponentName(Preferences.this,
FileUploader.class))) {
Log_OC.d(TAG, "Upload service connected");
mUploaderBinder = (FileUploader.FileUploaderBinder) service;
} else {
@ -926,7 +926,8 @@ public class Preferences extends PreferenceActivity
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))) {
} else if (component.equals(new ComponentName(Preferences.this,
FileUploader.class))) {
Log_OC.d(TAG, "Upload service suddenly disconnected");
mUploaderBinder = null;
}

View file

@ -363,8 +363,8 @@ public class UploadFilesActivity extends FileActivity implements
/**
* Updates the activity UI after the check of space is done.
*
* If there is not space enough. shows a new dialog to query the user if wants to move the files instead
* of copy them.
* If there is not space enough. shows a new dialog to query the user if wants to move the
* files instead of copy them.
*
* @param result 'True' when there is space enough to copy all the selected files.
*/
@ -398,7 +398,8 @@ public class UploadFilesActivity extends FileActivity implements
// to the ownCloud folder instead of copying
String[] args = {getString(R.string.app_name)};
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1, R.string.common_no
R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1,
R.string.common_no
);
dialog.setOnConfirmationListener(UploadFilesActivity.this);
dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);

View file

@ -0,0 +1,361 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* @author David A. Velasco
* @author masensio
* Copyright (C) 2016 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.ui.activity;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
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.CheckCurrentCredentialsOperation;
import com.owncloud.android.ui.fragment.UploadListFragment;
import com.owncloud.android.utils.MimetypeIconUtil;
import java.io.File;
/**
* Activity listing pending, active, and completed uploads. User can delete
* completed uploads from view. Content of this list of coming from
* {@link UploadsStorageManager}.
*
*/
public class UploadListActivity extends FileActivity implements UploadListFragment.ContainerActivity {
private static final String TAG = UploadListActivity.class.getSimpleName();
private static final String TAG_UPLOAD_LIST_FRAGMENT = "UPLOAD_LIST_FRAGMENT";
private UploadMessagesReceiver mUploadMessagesReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.upload_list_layout);
// this activity has no file really bound, it's for mulitple accounts at the same time; should no inherit
// from FileActivity; moreover, some behaviours inherited from FileActivity should be delegated to Fragments;
// but that's other story
setFile(null);
// Navigation Drawer
initDrawer();
// Add fragment with a transaction for setting a tag
if(savedInstanceState == null) {
createUploadListFragment();
} // else, the Fragment Manager makes the job on configuration changes
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setHomeButtonEnabled(true);
mDrawerToggle.setDrawerIndicatorEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.uploads_view_title);
}
private void createUploadListFragment(){
UploadListFragment uploadList = new UploadListFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.upload_list_fragment, uploadList, TAG_UPLOAD_LIST_FRAGMENT);
transaction.commit();
}
@Override
protected void onResume() {
Log_OC.v(TAG, "onResume() start");
super.onResume();
// Listen for upload messages
mUploadMessagesReceiver = new UploadMessagesReceiver();
IntentFilter uploadIntentFilter = new IntentFilter();
uploadIntentFilter.addAction(FileUploader.getUploadsAddedMessage());
uploadIntentFilter.addAction(FileUploader.getUploadStartMessage());
uploadIntentFilter.addAction(FileUploader.getUploadFinishMessage());
registerReceiver(mUploadMessagesReceiver, uploadIntentFilter);
Log_OC.v(TAG, "onResume() end");
}
@Override
protected void onPause() {
Log_OC.v(TAG, "onPause() start");
if (mUploadMessagesReceiver != null) {
unregisterReceiver(mUploadMessagesReceiver);
mUploadMessagesReceiver = null;
}
super.onPause();
Log_OC.v(TAG, "onPause() end");
}
// ////////////////////////////////////////
// UploadListFragment.ContainerActivity
// ////////////////////////////////////////
@Override
public boolean onUploadItemClick(OCUpload file) {
/// TODO is this path still active?
File f = new File(file.getLocalPath());
if(!f.exists()) {
Toast.makeText(this, "Cannot open. Local file does not exist.",
Toast.LENGTH_SHORT).show();
} else {
openFileWithDefault(file.getLocalPath());
}
return true;
}
/**
* Open file with app associates with its MIME type. If MIME type unknown, show list with all apps.
*/
private void openFileWithDefault(String localPath) {
Intent myIntent = new Intent(android.content.Intent.ACTION_VIEW);
File file = new File(localPath);
String mimetype = MimetypeIconUtil.getBestMimeTypeByFilename(localPath);
if ("application/octet-stream".equals(mimetype)) {
mimetype = "*/*";
}
myIntent.setDataAndType(Uri.fromFile(file), mimetype);
try {
startActivity(myIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "Found no app to open this file.", Toast.LENGTH_LONG).show();
Log_OC.i(TAG, "Could not find app for sending log history.");
}
}
/**
* Same as openFileWithDefault() but user cannot save default app.
* @param ocFile
*/
private void openFileWithDefaultNoDefault(OCFile ocFile) {
getFileOperationsHelper().openFile(ocFile);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
UploadsStorageManager storageManager = null;
UploadListFragment uploadListFragment =
(UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
switch (item.getItemId()) {
case android.R.id.home:
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
mDrawerLayout.openDrawer(GravityCompat.START);
}
break;
case R.id.action_retry_uploads:
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.retryFailedUploads(this, null, null);
break;
case R.id.action_clear_failed_uploads:
storageManager = new UploadsStorageManager(getContentResolver());
storageManager.clearFailedUploads();
uploadListFragment.updateUploads();
break;
case R.id.action_clear_successfull_uploads:
storageManager = new UploadsStorageManager(getContentResolver());
storageManager.clearSuccessfulUploads();
uploadListFragment.updateUploads();
break;
case R.id.action_clear_finished_uploads:
storageManager = new UploadsStorageManager(getContentResolver());
storageManager.clearAllFinishedUploads();
uploadListFragment.updateUploads();
break;
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.upload_list_menu, menu);
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FileActivity.REQUEST_CODE__UPDATE_CREDENTIALS && resultCode == RESULT_OK) {
// Retry uploads of the updated account
Account account = AccountUtils.getOwnCloudAccountByName(
this,
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.retryFailedUploads(
this,
account,
UploadResult.CREDENTIAL_ERROR
);
}
}
/**
*
* @param operation Operation performed.
* @param result Result of the removal.
*/
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof CheckCurrentCredentialsOperation) {
// Do not call super in this case; more refactoring needed around onRemoteOeprationFinish :'(
getFileOperationsHelper().setOpIdWaitingFor(Long.MAX_VALUE);
dismissLoadingDialog();
Account account = (Account) result.getData().get(0);
if (!result.isSuccess()) {
requestCredentialsUpdate(this, account);
} else {
// already updated -> just retry!
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.retryFailedUploads(this, account, UploadResult.CREDENTIAL_ERROR);
}
} else {
super.onRemoteOperationFinish(operation, result);
}
}
@Override
protected ServiceConnection newTransferenceServiceConnection() {
return new UploadListServiceConnection();
}
/** Defines callbacks for service binding, passed to bindService() */
private class UploadListServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName component, IBinder service) {
if (service instanceof FileUploaderBinder) {
if(mUploaderBinder == null)
{
mUploaderBinder = (FileUploaderBinder) service;
Log_OC.d(TAG, "UploadListActivity connected to Upload service. component: " +
component + " service: "
+ service);
// Say to UploadListFragment that the Binder is READY in the Activity
UploadListFragment uploadListFragment =
(UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
if (uploadListFragment != null) {
uploadListFragment.binderReady();
}
} else {
Log_OC.d(TAG, "mUploaderBinder already set. mUploaderBinder: " +
mUploaderBinder + " service:" + service);
}
} else {
Log_OC.d(TAG, "UploadListActivity not connected to Upload service. component: " +
component + " service: " + service);
return;
}
}
@Override
public void onServiceDisconnected(ComponentName component) {
if (component.equals(new ComponentName(UploadListActivity.this, FileUploader.class))) {
Log_OC.d(TAG, "UploadListActivity suddenly disconnected from Upload service");
mUploaderBinder = null;
}
}
};
/**
* Once the file upload has changed its status -> update uploads list view
*/
private class UploadMessagesReceiver extends BroadcastReceiver {
/**
* {@link BroadcastReceiver} to enable syncing feedback in UI
*/
@Override
public void onReceive(Context context, Intent intent) {
try {
UploadListFragment uploadListFragment =
(UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
uploadListFragment.updateUploads();
} finally {
if (intent != null) {
removeStickyBroadcast(intent);
}
}
}
}
@Override
protected String getDefaultTitle() {
return getString(R.string.uploads_view_title);
}
/**
* Called when the ownCloud {@link Account} associated to the Activity was just updated.
*/
@Override
protected void onAccountSet(boolean stateWasRecovered) {
super.onAccountSet(stateWasRecovered);
updateActionBarTitleAndHomeButton(null);
if (mAccountWasSet) {
setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount());
}
}
}

View file

@ -1,51 +1,30 @@
/**
* ownCloud Android client application
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author masensio
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* @author Bartek Przybylski
* @author masensio
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2016 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 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/>.
* 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.ui.activity;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;
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.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.RefreshFolderOperation;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
@ -62,6 +41,7 @@ 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;
@ -69,6 +49,8 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -81,7 +63,18 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;
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.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.CreateFolderOperation;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.adapter.UploaderAdapter;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
@ -90,6 +83,14 @@ import com.owncloud.android.utils.CopyTmpFileAsyncTask;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;
/**
* This can be used to upload things to an ownCloud instance.
@ -114,13 +115,13 @@ public class Uploader extends FileActivity
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 DIALOG_MULTIPLE_ACCOUNT = 3;
private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
private final static int REQUEST_CODE__SETUP_ACCOUNT = REQUEST_CODE__LAST_SHARED + 1;
private final static String KEY_PARENTS = "PARENTS";
private final static String KEY_FILE = "FILE";
@ -200,7 +201,7 @@ public class Uploader extends FileActivity
@Override
protected void onSaveInstanceState(Bundle outState) {
Log_OC.d(TAG, "onSaveInstanceState() start");
Log_OC.d(TAG, "onSaveInstanceState() start");
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_PARENTS, mParents);
//outState.putParcelable(KEY_ACCOUNT, mAccount);
@ -245,7 +246,8 @@ public class Uploader extends FileActivity
builder.setIcon(R.drawable.ic_warning);
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)));
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
@ -253,13 +255,13 @@ public class Uploader extends FileActivity
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ECLAIR_MR1) {
// using string value since in API7 this
// constant is not defined
// in API7 < this constant is defined in
// constatn is not defined
// in API7 < this constatant is defined in
// Settings.ADD_ACCOUNT_SETTINGS
// and Settings.EXTRA_AUTHORITIES
Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() });
startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
intent.putExtra("authorities", new String[]{MainApp.getAuthTokenType()});
startActivityForResult(intent, REQUEST_CODE__SETUP_ACCOUNT);
} else {
// since in API7 there is no direct call for
// account setup, so we need to
@ -267,7 +269,7 @@ public class Uploader extends FileActivity
// desired results and setup
// everything for ourself
Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
startActivityForResult(intent, REQUEST_CODE__SETUP_ACCOUNT);
}
}
});
@ -328,7 +330,7 @@ public class Uploader extends FileActivity
EditText mDirname;
public a(String path, EditText dirname) {
mPath = path;
mPath = path;
mDirname = dirname;
}
@ -379,24 +381,24 @@ public class Uploader extends FileActivity
public void onClick(View v) {
// 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
for (String p : mParents)
mUploadPath += p + OCFile.PATH_SEPARATOR;
Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
case R.id.uploader_choose_folder:
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();
uploadFiles();
break;
case R.id.uploader_cancel:
finish();
break;
default:
throw new IllegalArgumentException("Wrong element clicked");
break;
case R.id.uploader_cancel:
finish();
break;
default:
throw new IllegalArgumentException("Wrong element clicked");
}
}
@ -404,7 +406,7 @@ public class Uploader extends FileActivity
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
if (requestCode == REQUEST_CODE__SETUP_ACCOUNT) {
dismissDialog(DIALOG_NO_ACCOUNT);
if (resultCode == RESULT_CANCELED) {
finish();
@ -424,14 +426,13 @@ public class Uploader extends FileActivity
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("")){
if (current_dir.equals("")) {
getSupportActionBar().setTitle(getString(R.string.default_display_name_for_root_folder));
}
else{
} else {
getSupportActionBar().setTitle(current_dir);
}
boolean notRoot = (mParents.size() > 1);
@ -453,7 +454,7 @@ public class Uploader extends FileActivity
h.put("dirname", f);
data.add(h);
}
UploaderAdapter sa = new UploaderAdapter(this,
data,
R.layout.uploader_list_item_layout,
@ -464,10 +465,10 @@ public class Uploader extends FileActivity
mListView.setAdapter(sa);
Button btnChooseFolder = (Button) findViewById(R.id.uploader_choose_folder);
btnChooseFolder.setOnClickListener(this);
Button btnNewFolder = (Button) findViewById(R.id.uploader_cancel);
btnNewFolder.setOnClickListener(this);
mListView.setOnItemClickListener(this);
}
}
@ -512,85 +513,81 @@ public class Uploader extends FileActivity
return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
}
@SuppressLint("NewApi")
public void uploadFiles() {
try {
// ArrayList for files with path in external storage
ArrayList<String> local = new ArrayList<String>();
ArrayList<String> remote = new ArrayList<String>();
// this checks the mimeType
// this checks the mimeType
for (Parcelable mStream : mStreamsToUpload) {
Uri uri = (Uri) mStream;
String data = null;
String filePath = "";
if (uri != null) {
if (uri.getScheme().equals("content")) {
String mimeType = getContentResolver().getType(uri);
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);
local.add(data);
remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
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);
local.add(data);
remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.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);
data = c.getString(index);
local.add(data);
remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.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);
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 {
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 (uri.getScheme().equals("file")) {
filePath = Uri.decode(uri.toString()).replace(uri.getScheme() +
"://", "");
if (filePath.contains("mnt")) {
String splitedFilePath[] = filePath.split("/mnt");
filePath = splitedFilePath[1];
String splitedFilePath[] = filePath.split("/mnt");
filePath = splitedFilePath[1];
}
final File file = new File(filePath);
data = file.getAbsolutePath();
filePath = mUploadPath + file.getName();
}
else {
} else {
throw new SecurityException();
}
if (data == null) {
//if (data == null) {
mRemoteCacheData.add(filePath);
CopyTmpFileAsyncTask copyTask = new CopyTmpFileAsyncTask(this);
Object[] params = {uri, filePath, mRemoteCacheData.size() - 1,
@ -598,20 +595,14 @@ public class Uploader extends FileActivity
mNumCacheFile++;
showWaitingCopyDialog();
copyTask.execute(params);
}
}
else {
//} else {
// TODO request to FileUploader with data as source file, resulting in lazy temporary copy
//}
} 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, getAccount());
startService(intent);
//Save the path to shared preferences
SharedPreferences.Editor appPrefs = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
@ -620,31 +611,31 @@ public class Uploader extends FileActivity
finish();
}
} catch (SecurityException e) {
String message = String.format(getString(R.string.uploader_error_forbidden_content),
getString(R.string.app_name));
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
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);
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.
*
* @param operation Creation operation performed.
* @param result Result of the creation.
*/
private void onCreateFolderOperationFinish(CreateFolderOperation operation,
RemoteOperationResult result) {
@ -652,44 +643,44 @@ public class Uploader extends FileActivity
populateDirectoryList();
} else {
try {
Toast msg = Toast.makeText(this,
ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
Toast.LENGTH_LONG);
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);
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.
* Loads the target folder initialize shown to the user.
* <p/>
* The target account has to be chosen before this method is called.
*/
private void initTargetFolder() {
if (getStorageManager() == null) {
throw new IllegalStateException("Do not call this method before " +
"initializing mStorageManager");
}
SharedPreferences appPreferences = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext());
String last_path = appPreferences.getString("last_upload_path", "");
// "/" equals root-directory
if(last_path.equals("/")) {
if (last_path.equals("/")) {
mParents.add("");
} else{
} else {
String[] dir_names = last_path.split("/");
mParents.clear();
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(!getStorageManager().fileExists(generatePath(mParents)) && mParents.size() > 1){
while (!getStorageManager().fileExists(generatePath(mParents)) && mParents.size() > 1) {
mParents.pop();
}
}
@ -702,7 +693,7 @@ public class Uploader extends FileActivity
menu.findItem(R.id.action_sync_account).setVisible(false);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
@ -714,7 +705,7 @@ public class Uploader extends FileActivity
CreateFolderDialogFragment.CREATE_FOLDER_FRAGMENT);
break;
case android.R.id.home:
if((mParents.size() > 1)) {
if ((mParents.size() > 1)) {
onBackPressed();
}
break;
@ -824,21 +815,26 @@ public class Uploader extends FileActivity
}
/**
* Process the result of CopyTmpFileAsyncTask
*
* @param result
* @param index
*/
@Override
public void onTmpFileCopied(String result, int index) {
if (mNumCacheFile -- == 0) {
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);
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
this, getAccount(),
result,
mRemoteCacheData.get(index),
FileUploader.LOCAL_BEHAVIOUR_FORGET,
null, // MIME type will be detected from file name
false, // do not create parent folder if not existent
UploadFileOperation.CREATED_BY_USER
);
} else {
String message = String.format(getString(R.string.uploader_error_forbidden_content),
@ -865,7 +861,7 @@ public class Uploader extends FileActivity
/**
* Dismiss waiting for copy dialog
*/
public void dismissWaitingCopyDialog(){
public void dismissWaitingCopyDialog() {
Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_COPY_FILE);
if (frag != null) {
LoadingDialog loading = (LoadingDialog) frag;

View file

@ -0,0 +1,720 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* @author masensio
* Copyright (C) 2016 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.ui.adapter;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.MimetypeIconUtil;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Observable;
import java.util.Observer;
/**
* This Adapter populates a ListView with following types of uploads: pending,
* active, completed. Filtering possible.
*
*/
public class ExpandableUploadListAdapter extends BaseExpandableListAdapter implements Observer {
private static final String TAG = ExpandableUploadListAdapter.class.getSimpleName();
private FileActivity mParentActivity;
private UploadsStorageManager mUploadsStorageManager;
public ProgressListener mProgressListener;
interface Refresh {
public void refresh();
}
abstract class UploadGroup implements Refresh {
OCUpload[] items;
String name;
public UploadGroup(String groupName) {
this.name = groupName;
items = new OCUpload[0];
}
public String getGroupName() {
return name;
}
public Comparator<OCUpload> comparator = new Comparator<OCUpload>() {
@Override
public int compare(OCUpload upload1, OCUpload upload2) {
if (upload1.getUploadStatus().equals(UploadStatus.UPLOAD_IN_PROGRESS)) {
FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
if (binder != null) {
if (binder.isUploadingNow(upload1)) {
return -1;
} else if (binder.isUploadingNow(upload2)) {
return 1;
}
}
}
if (upload1.getUploadEndTimestamp() == 0) {
return compareUploadId(upload1, upload2);
} else {
return compareUpdateTime(upload1, upload2);
}
}
private int compareUploadId(OCUpload upload1, OCUpload upload2) {
return Long.valueOf(upload1.getUploadId()).compareTo(upload2.getUploadId());
}
private int compareUpdateTime(OCUpload upload1, OCUpload upload2) {
return Long.valueOf(upload2.getUploadEndTimestamp()).compareTo(upload1.getUploadEndTimestamp());
}
};
abstract public int getGroupIcon();
}
private UploadGroup[] mUploadGroups = null;
public ExpandableUploadListAdapter(FileActivity parentActivity) {
Log_OC.d(TAG, "ExpandableUploadListAdapter");
mParentActivity = parentActivity;
mUploadsStorageManager = new UploadsStorageManager(mParentActivity.getContentResolver());
mUploadGroups = new UploadGroup[3];
mUploadGroups[0] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_current_uploads)) {
@Override
public void refresh() {
items = mUploadsStorageManager.getCurrentAndPendingUploads();
Arrays.sort(items, comparator);
}
@Override
public int getGroupIcon() {
return R.drawable.upload_in_progress;
}
};
mUploadGroups[1] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_failed_uploads)) {
@Override
public void refresh() {
items = mUploadsStorageManager.getFailedUploads();
Arrays.sort(items, comparator);
}
@Override
public int getGroupIcon() {
return R.drawable.upload_failed;
}
};
mUploadGroups[2] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_finished_uploads)) {
@Override
public void refresh() {
items = mUploadsStorageManager.getFinishedUploads();
Arrays.sort(items, comparator);
}
@Override
public int getGroupIcon() {
return R.drawable.upload_finished;
}
};
loadUploadItemsFromDb();
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
mUploadsStorageManager.addObserver(this);
Log_OC.d(TAG, "registerDataSetObserver");
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
super.unregisterDataSetObserver(observer);
mUploadsStorageManager.deleteObserver(this);
Log_OC.d(TAG, "unregisterDataSetObserver");
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
private View getView(OCUpload[] uploadsItems, int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater inflator =
(LayoutInflater) mParentActivity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE
);
view = inflator.inflate(R.layout.upload_list_item, parent, false);
}
if (uploadsItems != null && uploadsItems.length > position) {
final OCUpload upload = uploadsItems[position];
// local file name
TextView fileTextView = (TextView) view.findViewById(R.id.upload_name);
File remoteFile = new File(upload.getRemotePath());
String fileName = remoteFile.getName();
if (fileName.length() == 0) {
fileName = File.separator;
}
fileTextView.setText(fileName);
// remote path to parent folder
TextView pathTextView = (TextView) view.findViewById(R.id.upload_remote_path);
String remoteParentPath = upload.getRemotePath();
remoteParentPath = new File(remoteParentPath).getParent();
pathTextView.setText(mParentActivity.getString(R.string.app_name) + remoteParentPath);
// file size
TextView fileSizeTextView = (TextView) view.findViewById(R.id.upload_file_size);
fileSizeTextView.setText(DisplayUtils.bytesToHumanReadable(upload.getFileSize()) + ", ");
//* upload date
TextView uploadDateTextView = (TextView) view.findViewById(R.id.upload_date);
long updateTime = upload.getUploadEndTimestamp();
CharSequence dateString = DisplayUtils.getRelativeDateTimeString(
mParentActivity,
updateTime,
DateUtils.SECOND_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,
0
);
uploadDateTextView.setText(dateString);
TextView accountNameTextView = (TextView) view.findViewById(R.id.upload_account);
accountNameTextView.setText(upload.getAccountName());
TextView statusTextView = (TextView) view.findViewById(R.id.upload_status);
ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.upload_progress_bar);
/// Reset fields visibility
uploadDateTextView.setVisibility(View.VISIBLE);
pathTextView.setVisibility(View.VISIBLE);
fileSizeTextView.setVisibility(View.VISIBLE);
accountNameTextView.setVisibility(View.VISIBLE);
statusTextView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
/// Update information depending of upload details
String status = getStatusText(upload);
switch (upload.getUploadStatus()) {
case UPLOAD_IN_PROGRESS:
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
if (binder != null) {
if (binder.isUploadingNow(upload)) {
/// really uploading, so...
/// ... unbind the old progress bar, if any; ...
if (mProgressListener != null) {
binder.removeDatatransferProgressListener(
mProgressListener,
mProgressListener.getUpload() // the one that was added
);
}
/// ... then, bind the current progress bar to listen for updates
mProgressListener = new ProgressListener(upload, progressBar);
binder.addDatatransferProgressListener(
mProgressListener,
upload
);
} else {
/// not really uploading; stop listening progress if view is reused!
if (convertView != null &&
mProgressListener != null &&
mProgressListener.isWrapping(progressBar)) {
binder.removeDatatransferProgressListener(
mProgressListener,
mProgressListener.getUpload() // the one that was added
);
mProgressListener = null;
}
}
} else {
Log_OC.w(
TAG,
"FileUploaderBinder not ready yet for upload " + upload.getRemotePath()
);
}
uploadDateTextView.setVisibility(View.GONE);
pathTextView.setVisibility(View.GONE);
fileSizeTextView.setVisibility(View.GONE);
progressBar.invalidate();
break;
case UPLOAD_FAILED:
uploadDateTextView.setVisibility(View.GONE);
break;
case UPLOAD_SUCCEEDED:
statusTextView.setVisibility(View.GONE);
break;
}
statusTextView.setText(status);
/// bind listeners to perform actions
ImageButton rightButton = (ImageButton) view.findViewById(R.id.upload_right_button);
if (upload.getUploadStatus() == UploadStatus.UPLOAD_IN_PROGRESS) {
//Cancel
rightButton.setImageResource(R.drawable.ic_cancel);
rightButton.setVisibility(View.VISIBLE);
rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FileUploader.FileUploaderBinder uploaderBinder = mParentActivity.getFileUploaderBinder();
if (uploaderBinder != null) {
uploaderBinder.cancel(upload);
refreshView();
}
}
});
} else if (upload.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
//Delete
rightButton.setImageResource(R.drawable.ic_action_delete);
rightButton.setVisibility(View.VISIBLE);
rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mUploadsStorageManager.removeUpload(upload);
refreshView();
}
});
} else { // UploadStatus.UPLOAD_SUCCESS
rightButton.setVisibility(View.INVISIBLE);
}
// retry
if (upload.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
if (UploadResult.CREDENTIAL_ERROR.equals(upload.getLastResult())) {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mParentActivity.getFileOperationsHelper().checkCurrentCredentials(
upload.getAccount(mParentActivity)
);
}
});
} else {
// not a credentials error
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
File file = new File(upload.getLocalPath());
if (file.exists()) {
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.retry(mParentActivity, upload);
refreshView();
} else {
final String message = String.format(
mParentActivity.getString(R.string.local_file_not_found_toast)
);
Toast.makeText(mParentActivity, message, Toast.LENGTH_SHORT).show();
}
}
});
}
} else {
view.setOnClickListener(null);
}
/// Set icon or thumbnail
ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
fileIcon.setImageResource(R.drawable.file);
/** Cancellation needs do be checked and done before changing the drawable in fileIcon, or
* {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task.
**/
OCFile fakeFileToCheatThumbnailsCacheManagerInterface = new OCFile(upload.getRemotePath());
fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(upload.getLocalPath());
fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(upload.getMimeType());
boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialWork(
fakeFileToCheatThumbnailsCacheManagerInterface,
fileIcon)
);
// TODO this code is duplicated; refactor to a common place
if ((fakeFileToCheatThumbnailsCacheManagerInterface.isImage()
&& fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null &&
upload.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED)) {
// Thumbnail in Cache?
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId())
);
if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.needsUpdateThumbnail()) {
fileIcon.setImageBitmap(thumbnail);
} else {
// generate new Thumbnail
if (allowedToCreateNewThumbnail) {
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
new ThumbnailsCacheManager.ThumbnailGenerationTask(
fileIcon, mParentActivity.getStorageManager(), mParentActivity.getAccount()
);
if (thumbnail == null) {
thumbnail = ThumbnailsCacheManager.mDefaultImg;
}
final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncDrawable(
mParentActivity.getResources(),
thumbnail,
task
);
fileIcon.setImageDrawable(asyncDrawable);
task.execute(fakeFileToCheatThumbnailsCacheManagerInterface);
}
}
if ("image/png".equals(upload.getMimeType())) {
fileIcon.setBackgroundColor(mParentActivity.getResources()
.getColor(R.color.background_color));
}
} else if (fakeFileToCheatThumbnailsCacheManagerInterface.isImage()) {
File file = new File(upload.getLocalPath());
// Thumbnail in Cache?
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
String.valueOf(file.hashCode()));
if (thumbnail != null) {
fileIcon.setImageBitmap(thumbnail);
} else {
// generate new Thumbnail
if (allowedToCreateNewThumbnail) {
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon);
if (thumbnail == null) {
thumbnail = ThumbnailsCacheManager.mDefaultImg;
}
final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncDrawable(
mParentActivity.getResources(),
thumbnail,
task
);
fileIcon.setImageDrawable(asyncDrawable);
task.execute(file);
Log_OC.v(TAG, "Executing task to generate a new thumbnail");
}
}
if ("image/png".equalsIgnoreCase(upload.getMimeType())) {
fileIcon.setBackgroundColor(mParentActivity.getResources()
.getColor(R.color.background_color));
}
} else {
fileIcon.setImageResource(MimetypeIconUtil.getFileTypeIconId(
upload.getMimeType(),
fileName
));
}
}
return view;
}
/**
* Gets the status text to show to the user according to the status and last result of the
* the given upload.
*
* @param upload Upload to describe.
* @return Text describing the status of the given upload.
*/
private String getStatusText(OCUpload upload) {
String status;
switch (upload.getUploadStatus()) {
case UPLOAD_IN_PROGRESS:
status = mParentActivity.getString(R.string.uploads_view_later_waiting_to_upload);
FileUploader.FileUploaderBinder binder = mParentActivity.getFileUploaderBinder();
if (binder != null && binder.isUploadingNow(upload)) {
/// really uploading, bind the progress bar to listen for progress updates
status = mParentActivity.getString(R.string.uploader_upload_in_progress_ticker);
}
break;
case UPLOAD_SUCCEEDED:
status = mParentActivity.getString(R.string.uploads_view_upload_status_succeeded);
break;
case UPLOAD_FAILED:
switch (upload.getLastResult()) {
case CREDENTIAL_ERROR:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_credentials_error
);
break;
case FOLDER_ERROR:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_folder_error
);
break;
case FILE_NOT_FOUND:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_localfile_error
);
break;
case FILE_ERROR:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_file_error
);
break;
case PRIVILEDGES_ERROR:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_permission_error
);
break;
case NETWORK_CONNECTION:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_failed_connection_error
);
break;
case DELAYED_FOR_WIFI:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_waiting_for_wifi
);
break;
case CONFLICT_ERROR:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_conflict
);
break;
case SERVICE_INTERRUPTED:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_service_interrupted
);
break;
case UNKNOWN:
status = mParentActivity.getString(
R.string.uploads_view_upload_status_unknown_fail
);
break;
case CANCELLED:
// should not get here ; cancelled uploads should be wiped out
status = mParentActivity.getString(
R.string.uploads_view_upload_status_cancelled
);
break;
case UPLOADED:
// should not get here ; status should be UPLOAD_SUCCESS
status = mParentActivity.getString(R.string.uploads_view_upload_status_succeeded);
break;
default:
status = "Naughty devs added a new fail result but no description for the user";
break;
}
break;
default:
status = "Uncontrolled status: " + upload.getUploadStatus().toString();
}
return status;
}
@Override
public boolean hasStableIds() {
return false;
}
/**
* Load upload items from {@link UploadsStorageManager}.
*/
private void loadUploadItemsFromDb() {
Log_OC.d(TAG, "loadUploadItemsFromDb");
for (UploadGroup group : mUploadGroups) {
group.refresh();
}
notifyDataSetChanged();
}
@Override
public void update(Observable arg0, Object arg1) {
Log_OC.d(TAG, "update");
loadUploadItemsFromDb();
}
public void refreshView() {
Log_OC.d(TAG, "refreshView");
loadUploadItemsFromDb();
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return mUploadGroups[(int) getGroupId(groupPosition)].items[childPosition];
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView,
ViewGroup parent) {
return getView(mUploadGroups[(int) getGroupId(groupPosition)].items, childPosition, convertView, parent);
}
@Override
public int getChildrenCount(int groupPosition) {
return mUploadGroups[(int) getGroupId(groupPosition)].items.length;
}
@Override
public Object getGroup(int groupPosition) {
return mUploadGroups[(int) getGroupId(groupPosition)];
}
@Override
public int getGroupCount() {
int size = 0;
for (UploadGroup uploadGroup : mUploadGroups) {
if (uploadGroup.items.length > 0) {
size++;
}
}
return size;
}
/**
* Returns the groupId (that is, index in mUploadGroups) for group at position groupPosition (0-based).
* Could probably be done more intuitive but this tested methods works as intended.
*/
@Override
public long getGroupId(int groupPosition) {
int id = -1;
for (int i = 0; i <= groupPosition; ) {
id++;
if (mUploadGroups[id].items.length > 0) {
i++;
}
}
return id;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
//force group to stay unfolded
ExpandableListView listView = (ExpandableListView) parent;
listView.expandGroup(groupPosition);
listView.setGroupIndicator(null);
UploadGroup group = (UploadGroup) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater inflaInflater = (LayoutInflater) mParentActivity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflaInflater.inflate(R.layout.upload_list_group, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.uploadListGroupName);
tv.setText(group.getGroupName());
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public class ProgressListener implements OnDatatransferProgressListener {
int mLastPercent = 0;
OCUpload mUpload = null;
WeakReference<ProgressBar> mProgressBar = null;
public ProgressListener(OCUpload upload, ProgressBar progressBar) {
mUpload = upload;
mProgressBar = new WeakReference<ProgressBar>(progressBar);
}
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String
filename) {
int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
if (percent != mLastPercent) {
ProgressBar pb = mProgressBar.get();
if (pb != null) {
pb.setProgress(percent);
pb.postInvalidate();
}
}
mLastPercent = percent;
}
public boolean isWrapping(ProgressBar progressBar) {
ProgressBar wrappedProgressBar = mProgressBar.get();
return (
wrappedProgressBar != null &&
wrappedProgressBar == progressBar // on purpose; don't replace with equals
);
}
public OCUpload getUpload() {
return mUpload;
}
}
public void addBinder() {
notifyDataSetChanged();
}
}

View file

@ -6,7 +6,7 @@
* @author David A. Velasco
* @author masensio
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -24,9 +24,6 @@
package com.owncloud.android.ui.adapter;
import java.io.File;
import java.util.Vector;
import android.accounts.Account;
import android.content.Context;
import android.content.SharedPreferences;
@ -57,6 +54,8 @@ import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimetypeIconUtil;
import java.util.Vector;
/**
* This Adapter populates a ListView with all files and folders in an ownCloud
@ -88,12 +87,14 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
mJustFolders = justFolders;
mContext = context;
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
mTransferServiceGetter = transferServiceGetter;
mAppPreferences = PreferenceManager
.getDefaultSharedPreferences(mContext);
// Read sorting order, default to sort by name ascending
FileStorageUtils.mSortOrder = mAppPreferences.getInt("sortOrder", 0);
FileStorageUtils.mSortAscending = mAppPreferences.getBoolean("sortAscending", true);
@ -356,6 +357,7 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
file.getFileName()));
}
} else {
// Folder
fileIcon.setImageResource(

View file

@ -1,138 +0,0 @@
/**
* 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,
* 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.ui.adapter;
import android.app.Activity;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.owncloud.android.R;
public class MyExpandableListAdapter extends BaseExpandableListAdapter {
private final SparseArray<GroupAdapter> groups;
public LayoutInflater inflater;
public Activity activity;
public MyExpandableListAdapter(Activity act, SparseArray<GroupAdapter> groups) {
activity = act;
this.groups = groups;
inflater = act.getLayoutInflater();
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return groups.get(groupPosition).children.get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final String children = (String) getChild(groupPosition, childPosition);
TextView text = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_details, null);
}
text = (TextView) convertView.findViewById(R.id.textView1);
text.setText(children);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, children, Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_group, null);
}
final GroupAdapter groupAdapter = (GroupAdapter) getGroup(groupPosition);
if (groupAdapter.children.size() == 0){
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, groupAdapter.string, Toast.LENGTH_SHORT).show();
}
});
}
((TextView) convertView).setText(groupAdapter.string);
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return groups.get(groupPosition).children.size();
}
@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
@Override
public int getGroupCount() {
return groups.size();
}
@Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
}
@Override
public void onGroupExpanded(int groupPosition) {
super.onGroupExpanded(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}

View file

@ -77,12 +77,12 @@ public class UploadSourceDialogFragment extends DialogFragment {
UploadFilesActivity.EXTRA_ACCOUNT,
((FileActivity)getActivity()).getAccount()
);
//startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
//startActivityForResult(action, REQUEST_CODE__SELECT_MULTIPLE_FILES);
// this flow seems broken;
// Actionbarsherlock, maybe?
getActivity().startActivityForResult(
action,
FileDisplayActivity.ACTION_SELECT_MULTIPLE_FILES
FileDisplayActivity.REQUEST_CODE__SELECT_MULTIPLE_FILES
);
} else if (item == 1) {
@ -94,7 +94,7 @@ public class UploadSourceDialogFragment extends DialogFragment {
}
getActivity().startActivityForResult(
Intent.createChooser(action, getString(R.string.upload_chooser_title)),
FileDisplayActivity.ACTION_SELECT_CONTENT_FROM_APPS
FileDisplayActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS
);
}
}

View file

@ -0,0 +1,45 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* Copyright (C) 2016 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.ui.errorhandling;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.owncloud.android.R;
public class ErrorShowActivity extends Activity {
private static final String TAG = ErrorShowActivity.class.getSimpleName();
TextView mError;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "ErrorShowActivity was called. See above for StackTrace.");
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
setContentView(R.layout.errorhandling_showerror);
mError = (TextView) findViewById(R.id.errorTextView);
mError.setText(getIntent().getStringExtra("error"));
}
}

View file

@ -0,0 +1,85 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* Copyright (C) 2016 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.ui.errorhandling;
import java.io.PrintWriter;
import java.io.StringWriter;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
public class ExceptionHandler implements java.lang.Thread.UncaughtExceptionHandler {
private final Activity mContext;
private final String LINE_SEPARATOR = "\n";
private static final String TAG = ExceptionHandler.class.getSimpleName();
public ExceptionHandler(Activity context) {
mContext = context;
}
public void uncaughtException(Thread thread, Throwable exception) {
Log.e(TAG, "ExceptionHandler caught UncaughtException", exception);
StringWriter stackTrace = new StringWriter();
exception.printStackTrace(new PrintWriter(stackTrace));
StringBuilder errorReport = new StringBuilder();
errorReport.append("************ CAUSE OF ERROR ************\n\n");
errorReport.append(stackTrace.toString());
errorReport.append("\n************ DEVICE INFORMATION ***********\n");
errorReport.append("Brand: ");
errorReport.append(Build.BRAND);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Device: ");
errorReport.append(Build.DEVICE);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Model: ");
errorReport.append(Build.MODEL);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Id: ");
errorReport.append(Build.ID);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Product: ");
errorReport.append(Build.PRODUCT);
errorReport.append(LINE_SEPARATOR);
errorReport.append("\n************ FIRMWARE ************\n");
errorReport.append("SDK: ");
errorReport.append(Build.VERSION.SDK_INT);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Release: ");
errorReport.append(Build.VERSION.RELEASE);
errorReport.append(LINE_SEPARATOR);
errorReport.append("Incremental: ");
errorReport.append(Build.VERSION.INCREMENTAL);
errorReport.append(LINE_SEPARATOR);
Log.e(TAG, "An exception was thrown and handled by ExceptionHandler:", exception);
Intent intent = new Intent(mContext, ErrorShowActivity.class);
intent.putExtra("error", errorReport.toString());
mContext.startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1000);
}
}

View file

@ -0,0 +1,90 @@
/**
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2016 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.ui.fragment;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.TextView;
import com.owncloud.android.R;
import com.owncloud.android.lib.common.utils.Log_OC;
/**
* Extending ExtendedListFragment. This allows dividing list in groups.
*/
public class ExpandableListFragment extends ExtendedListFragment implements OnChildClickListener
{
protected static final String TAG = ExpandableListFragment.class.getSimpleName();
protected ExpandableListView mList;
public void setListAdapter(ExpandableListAdapter listAdapter) {
mList.setAdapter(listAdapter);
mList.invalidate();
}
public ExpandableListView getListView() {
return mList;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log_OC.e(TAG, "onCreateView");
View v = inflater.inflate(R.layout.list_fragment_expandable, null);
mEmptyListMessage = (TextView) v.findViewById(R.id.empty_list_view);
mList = (ExpandableListView)(v.findViewById(R.id.list_root));
mList.setOnChildClickListener(this);
mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
mList.setDividerHeight(1);
// if (savedInstanceState != null) {
// int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
// setReferencePosition(referencePosition);
// }
// Pull down refresh
mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files);
mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files_emptyView);
onCreateSwipeToRefresh(mRefreshListLayout);
onCreateSwipeToRefresh(mRefreshEmptyLayout);
mList.setEmptyView(mRefreshEmptyLayout);
return v;
}
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
// to be @overriden
Log_OC.e(TAG, "onChildClick(). This method should be overriden!");
return false;
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2015 ownCloud Inc.
* Copyright (C) 2012-2016 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,
@ -20,8 +20,6 @@
package com.owncloud.android.ui.fragment;
import java.util.ArrayList;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@ -45,33 +43,33 @@ import com.owncloud.android.ui.ExtendedListView;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
import com.owncloud.android.ui.adapter.FileListListAdapter;
import java.util.ArrayList;
import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
/**
* TODO extending SherlockListFragment instead of SherlockFragment
*/
public class ExtendedListFragment extends Fragment
implements OnItemClickListener, OnEnforceableRefreshListener {
private static final String TAG = ExtendedListFragment.class.getSimpleName();
protected static final String TAG = ExtendedListFragment.class.getSimpleName();
protected static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
private static final String KEY_INDEXES = "INDEXES";
private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
private static final String KEY_TOPS = "TOPS";
private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
private SwipeRefreshLayout mRefreshListLayout;
protected SwipeRefreshLayout mRefreshListLayout;
private SwipeRefreshLayout mRefreshGridLayout;
private SwipeRefreshLayout mRefreshEmptyLayout;
private TextView mEmptyListMessage;
protected SwipeRefreshLayout mRefreshEmptyLayout;
protected TextView mEmptyListMessage;
private FloatingActionsMenu mFabMain;
private FloatingActionButton mFabUpload;
private FloatingActionButton mFabMkdir;
private FloatingActionButton mFabUploadFromApp;
// Save the state of the scroll in browsing
private ArrayList<Integer> mIndexes;
private ArrayList<Integer> mFirstPositions;
@ -386,7 +384,7 @@ public class ExtendedListFragment extends Fragment
return (mEmptyListMessage != null) ? mEmptyListMessage.getText().toString() : "";
}
private void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
protected void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
// Colors in animations
refreshLayout.setColorSchemeResources(R.color.color_accent, R.color.primary,
R.color.primary_dark);

View file

@ -4,7 +4,7 @@
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -549,6 +549,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
mContainerActivity.getFileUploaderBinder().
addDatatransferProgressListener(mProgressListener, mAccount, getFile());
}
} else {
Log_OC.d(TAG, "mProgressListener == null");
}
}

View file

@ -5,7 +5,7 @@
* @author masensio
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -229,7 +229,7 @@ public class OCFileListFragment extends ExtendedListFragment
@Override
public void onClick(View v) {
UploadFilesActivity.startUploadActivityForResult(getActivity(), ((FileActivity)getActivity())
.getAccount(), FileDisplayActivity.ACTION_SELECT_MULTIPLE_FILES);
.getAccount(), FileDisplayActivity.REQUEST_CODE__SELECT_MULTIPLE_FILES);
getFabMain().collapse();
recordMiniFabClick();
}
@ -285,7 +285,7 @@ public class OCFileListFragment extends ExtendedListFragment
}
getActivity().startActivityForResult(
Intent.createChooser(action, getString(R.string.upload_chooser_title)),
FileDisplayActivity.ACTION_SELECT_CONTENT_FROM_APPS
FileDisplayActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS
);
getFabMain().collapse();
recordMiniFabClick();
@ -586,7 +586,7 @@ public class OCFileListFragment extends ExtendedListFragment
// Pass mTargetFile that contains info of selected file/folder
action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES);
return true;
}
case R.id.action_favorite_file: {
@ -602,7 +602,7 @@ public class OCFileListFragment extends ExtendedListFragment
// Pass mTargetFile that contains info of selected file/folder
action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES);
return true;
default:
return false;

View file

@ -0,0 +1,152 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* Copyright (C) 2016 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.ui.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.ListView;
import com.owncloud.android.R;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.adapter.ExpandableUploadListAdapter;
/**
* A Fragment that lists all files and folders in a given LOCAL path.
*
*/
public class UploadListFragment extends ExpandableListFragment {
static private String TAG = UploadListFragment.class.getSimpleName();
/**
* Reference to the Activity which this fragment is attached to. For
* callbacks
*/
private UploadListFragment.ContainerActivity mContainerActivity;
private ExpandableUploadListAdapter mAdapter;
/** Is binder ready in the Activity? */
private boolean mBinderReady = false;
public void setBinderReady(boolean ready) {
mBinderReady = ready;
}
public boolean isBinderReady(){
return mBinderReady;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
setMessageForEmptyList(getString(R.string.upload_list_empty));
setOnRefreshListener(this);
return v;
}
@Override
public void onRefresh() {
// remove the progress circle as soon as pull is triggered, like in the list of files
mRefreshEmptyLayout.setRefreshing(false);
mRefreshListLayout.setRefreshing(false);
mAdapter.notifyDataSetChanged();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mContainerActivity = (ContainerActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement "
+ UploadListFragment.ContainerActivity.class.getSimpleName());
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log_OC.d(TAG, "onActivityCreated() start");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
Log_OC.d(TAG, "onStart() start");
super.onStart();
mAdapter = new ExpandableUploadListAdapter((FileActivity)getActivity());
setListAdapter(mAdapter);
}
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
boolean handled = false;
OCUpload OCUpload = (OCUpload) mAdapter.getChild(groupPosition, childPosition);
if (OCUpload != null) {
// notify the click to container Activity
handled = mContainerActivity.onUploadItemClick(OCUpload);
} else {
Log_OC.w(TAG, "Null object in ListAdapter!!");
}
return handled;
}
/**
* Interface to implement by any Activity that includes some instance of
* UploadListFragment
*
* @author LukeOwncloud
*/
public interface ContainerActivity {
/**
* Callback method invoked when an upload item is clicked by the user on
* the upload list
*
* @param file
* @return return true if click was handled.
*/
public boolean onUploadItemClick(OCUpload file);
}
public void binderReady(){
setBinderReady(true);
if (mAdapter != null) {
mAdapter.addBinder();
}
}
public void updateUploads(){
if (mAdapter != null) {
mAdapter.refreshView();
}
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -21,10 +21,6 @@ package com.owncloud.android.ui.preview;
import android.accounts.Account;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.support.v7.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@ -32,12 +28,16 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -557,7 +557,7 @@ public class PreviewMediaFragment extends FileFragment implements
i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
mVideoPreview.pause();
i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
startActivityForResult(i, 0);
startActivityForResult(i, FileActivity.REQUEST_CODE__LAST_SHARED + 1);
}
@Override

View file

@ -0,0 +1,47 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2016 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.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.owncloud.android.lib.common.utils.Log_OC;
public class ConnectivityUtils {
private final static String TAG = ConnectivityUtils.class.getName();
public static boolean isAppConnectedViaWiFi(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
boolean result =
cm != null && cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
&& cm.getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED;
Log_OC.d(TAG, "is AppConnectedViaWifi returns " + result);
return result;
}
public static boolean isAppConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
}

View file

@ -2,7 +2,7 @@
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2016 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,
@ -53,23 +53,53 @@ public class FileStorageUtils {
public static Integer mSortOrder = SORT_NAME;
public static Boolean mSortAscending = true;
/**
* Takes a full path to owncloud file and removes beginning which is path to ownload data folder.
* If fullPath does not start with that folder, fullPath is returned as is.
*/
public static final String removeDataFolderPath(String fullPath) {
File sdCard = Environment.getExternalStorageDirectory();
String dataFolderPath = sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/";
if(fullPath.indexOf(dataFolderPath) == 0) {
return fullPath.substring(dataFolderPath.length());
}
return fullPath;
}
/**
* Get local owncloud storage path for accountName.
*/
public static final String getSavePath(String accountName) {
File sdCard = Environment.getExternalStorageDirectory();
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names,
// that can be in the accountName since 0.1.190B
}
/**
* Get local path where OCFile file is to be stored after upload. That is,
* corresponding local path (in local owncloud storage) to remote uploaded
* file.
*/
public static final String getDefaultSavePathFor(String accountName, OCFile file) {
return getSavePath(accountName) + file.getRemotePath();
}
/**
* Get absolute path to tmp folder inside datafolder in sd-card for given accountName.
*/
public static final String getTemporalPath(String accountName) {
File sdCard = Environment.getExternalStorageDirectory();
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/tmp/" + Uri.encode(accountName, "@");
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names,
// that can be in the accountName since 0.1.190B
}
/**
* Optimistic number of bytes available on sd-card. accountName is ignored.
* @param accountName not used. can thus be null.
* @return Optimistic number of available bytes (can be less)
*/
@SuppressLint("NewApi")
public static final long getUsableSpace(String accountName) {
File savePath = Environment.getExternalStorageDirectory();

View file

@ -1,3 +1,22 @@
/**
* ownCloud Android client application
*
* Copyright (C) 2016 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.utils;
import android.webkit.MimeTypeMap;
@ -83,6 +102,21 @@ public class MimetypeIconUtil {
return R.drawable.ic_menu_archive;
}
/**
* Returns a single MIME type of all the possible, by inspection of the file extension, and taking
* into account the MIME types known by ownCloud first.
*
* @param filename Name of file
* @return A single MIME type, "application/octet-stream" for unknown file extensions.
*/
public static String getBestMimeTypeByFilename(String filename) {
List<String> candidates = determineMimeTypesByFilename(filename);
if (candidates == null || candidates.size() < 1) {
return "application/octet-stream";
}
return candidates.get(0);
}
/**
* determines the icon based on the mime type.
*

View file

@ -0,0 +1,53 @@
/**
* ownCloud Android client application
*
* @author LukeOwncloud
* Copyright (C) 2016 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.utils;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.os.BatteryManager;
public class UploadUtils {
public static boolean isCharging(Context context) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
return status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL;
}
public static boolean isOnline(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
public static boolean isConnectedViaWiFi(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm != null && cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
&& cm.getActiveNetworkInfo().getState() == State.CONNECTED;
}
}