Merge 8bd39d9438
into 81cc7eb5d7
100
build.gradle
|
@ -11,9 +11,10 @@ buildscript {
|
|||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.2'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-alpha9'
|
||||
classpath 'com.google.gms:google-services:3.0.0'
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +30,7 @@ configurations.all {
|
|||
}
|
||||
|
||||
ext {
|
||||
supportLibraryVersion = '25.0.0'
|
||||
supportLibraryVersion = '25.2.0'
|
||||
googleLibraryVersion = '10.2.4'
|
||||
|
||||
travisBuild = System.getenv("TRAVIS") == "true"
|
||||
|
@ -44,6 +45,7 @@ repositories {
|
|||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
google()
|
||||
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
|
@ -63,7 +65,7 @@ android {
|
|||
}
|
||||
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.0'
|
||||
buildToolsVersion '25.0.2'
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
@ -78,14 +80,18 @@ android {
|
|||
// adapt structure from Eclipse to Gradle/Android Studio expectations;
|
||||
// see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure
|
||||
|
||||
flavorDimensions "default"
|
||||
|
||||
productFlavors {
|
||||
// used for f-droid
|
||||
generic {
|
||||
applicationId 'com.nextcloud.client'
|
||||
dimension "default"
|
||||
}
|
||||
|
||||
gplay {
|
||||
applicationId 'com.nextcloud.client'
|
||||
dimension "default"
|
||||
}
|
||||
|
||||
modified {
|
||||
|
@ -94,6 +100,7 @@ android {
|
|||
// domain name
|
||||
// .client
|
||||
applicationId 'com.custom.client'
|
||||
dimension "default"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,11 +118,6 @@ android {
|
|||
preDexLibraries = preDexEnabled && !travisBuild
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/LICENSE'
|
||||
|
@ -143,10 +145,10 @@ android {
|
|||
xml.enabled = false
|
||||
html.enabled = true
|
||||
xml {
|
||||
destination "$project.buildDir/reports/pmd/pmd.xml"
|
||||
destination = file("$project.buildDir/reports/pmd/pmd.xml")
|
||||
}
|
||||
html {
|
||||
destination "$project.buildDir/reports/pmd/pmd.html"
|
||||
destination = file("$project.buildDir/reports/pmd/pmd.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,67 +167,63 @@ android {
|
|||
xml.enabled = false
|
||||
html.enabled = true
|
||||
html {
|
||||
destination "$project.buildDir/reports/findbugs/findbugs.html"
|
||||
destination = file("$project.buildDir/reports/findbugs/findbugs.html")
|
||||
}
|
||||
}
|
||||
classpath = files()
|
||||
}
|
||||
check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/// dependencies for app building
|
||||
compile name: 'touch-image-view'
|
||||
compile 'com.android.support:multidex:1.0.1'
|
||||
|
||||
compile 'com.github.nextcloud:android-library:1.0.22'
|
||||
compile "com.android.support:support-v4:${supportLibraryVersion}"
|
||||
compile "com.android.support:design:${supportLibraryVersion}"
|
||||
compile 'com.jakewharton:disklrucache:2.0.2'
|
||||
compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
|
||||
compile "com.android.support:cardview-v7:${supportLibraryVersion}"
|
||||
compile 'com.github.tobiasKaminsky:android-floating-action-button:1.10.2'
|
||||
compile 'com.google.code.findbugs:annotations:2.0.1'
|
||||
compile group: 'commons-io', name: 'commons-io', version: '2.4'
|
||||
compile 'com.github.evernote:android-job:v1.1.9'
|
||||
compile 'com.jakewharton:butterknife:8.4.0'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile 'com.googlecode.ez-vcard:ez-vcard:0.10.2'
|
||||
|
||||
implementation name: 'touch-image-view'
|
||||
implementation 'com.android.support:multidex:1.0.2'
|
||||
implementation 'com.github.nextcloud:android-library:1.0.23'
|
||||
implementation "com.android.support:support-v4:${supportLibraryVersion}"
|
||||
implementation "com.android.support:design:${supportLibraryVersion}"
|
||||
implementation 'com.jakewharton:disklrucache:2.0.2'
|
||||
implementation "com.android.support:appcompat-v7:${supportLibraryVersion}"
|
||||
implementation "com.android.support:cardview-v7:${supportLibraryVersion}"
|
||||
implementation "com.android.support:exifinterface:${supportLibraryVersion}"
|
||||
implementation 'com.github.tobiasKaminsky:android-floating-action-button:1.10.2'
|
||||
implementation 'com.google.code.findbugs:annotations:2.0.1'
|
||||
implementation 'commons-io:commons-io:2.5'
|
||||
implementation 'com.github.evernote:android-job:v1.1.11'
|
||||
implementation 'com.jakewharton:butterknife:8.5.1'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
|
||||
implementation 'org.greenrobot:eventbus:3.0.0'
|
||||
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.2'
|
||||
implementation 'org.lukhnos:nnio:0.2'
|
||||
// uncomment for gplay, modified
|
||||
// compile "com.google.firebase:firebase-messaging:${googleLibraryVersion}"
|
||||
// compile "com.google.android.gms:play-services-base:${googleLibraryVersion}"
|
||||
|
||||
compile 'org.parceler:parceler-api:1.1.6'
|
||||
// implementation "com.google.firebase:firebase-messaging:${googleLibraryVersion}"
|
||||
// implementation "com.google.android.gms:play-services-base:${googleLibraryVersion}"
|
||||
implementation 'org.parceler:parceler-api:1.1.6'
|
||||
annotationProcessor 'org.parceler:parceler:1.1.6'
|
||||
|
||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||
compile 'com.caverock:androidsvg:1.2.1'
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:3.7.0'
|
||||
implementation 'com.caverock:androidsvg:1.2.1'
|
||||
/// dependencies for local unit tests
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
/// dependencies for instrumented tests
|
||||
// JUnit4 Rules
|
||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||
|
||||
androidTestImplementation 'com.android.support.test:rules:0.5'
|
||||
// Android JUnit Runner
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
|
||||
androidTestImplementation 'com.android.support.test:runner:0.5'
|
||||
// Android Annotation Support
|
||||
androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
|
||||
|
||||
androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}"
|
||||
// Espresso core
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||
// UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests
|
||||
//androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
|
||||
//androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
|
||||
// fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details
|
||||
//androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
|
||||
|
||||
//androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}"
|
||||
implementation 'org.jetbrains:annotations:15.0'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
|
1
drawable_resources/ic_folder_star.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20,6H12L10,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8A2,2 0 0,0 20,6M17.94,17L15,15.28L12.06,17L12.84,13.67L10.25,11.43L13.66,11.14L15,8L16.34,11.14L19.75,11.43L17.16,13.67L17.94,17Z" /></svg>
|
After Width: | Height: | Size: 492 B |
1
gradle.properties
Normal file
|
@ -0,0 +1 @@
|
|||
android.enableAapt2=false
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Wed Mar 15 00:10:20 CET 2017
|
||||
#Fri May 05 19:09:31 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
|
1
lint.xml
|
@ -2,5 +2,6 @@
|
|||
<lint>
|
||||
<issue id="InvalidPackage">
|
||||
<ignore path="**/freemarker-2.3.23.jar"/>
|
||||
<ignore path="**/nnio-0.2.jar"/>
|
||||
</issue>
|
||||
</lint>
|
|
@ -1,2 +1,2 @@
|
|||
DO NOT TOUCH; GENERATED BY DRONE
|
||||
<span class="mdl-layout-title">Lint Report: 10 errors and 676 warnings</span>
|
||||
<span class="mdl-layout-title">Lint Report: 6 errors and 653 warnings</span>
|
||||
|
|
|
@ -167,7 +167,7 @@ public class PushUtils {
|
|||
remoteOperationResult = unregisterAccountDeviceForProxyOperation.execute(mClient);
|
||||
|
||||
if (remoteOperationResult.isSuccess()) {
|
||||
arbitraryDataProvider.deleteKeyForAccount(account, KEY_PUSH);
|
||||
arbitraryDataProvider.deleteKeyForAccount(account.name, KEY_PUSH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ public class PushUtils {
|
|||
PushConfigurationState pushArbitraryData = new PushConfigurationState(token,
|
||||
pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
|
||||
pushResponse.getPublicKey(), false);
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account, KEY_PUSH,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, KEY_PUSH,
|
||||
gson.toJson(pushArbitraryData));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
<activity android:name=".ui.activity.ParticipateActivity" />
|
||||
<activity android:name=".ui.activity.ActivitiesListActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden" />
|
||||
<activity android:name=".ui.activity.FolderSyncActivity" />
|
||||
<activity android:name=".ui.activity.SyncedFoldersActivity"/>
|
||||
<activity android:name=".ui.activity.UploadFilesActivity" />
|
||||
<activity android:name=".ui.activity.ExternalSiteWebView"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden" />
|
||||
|
@ -120,6 +120,11 @@
|
|||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.ownCloud.Fullscreen" />
|
||||
|
||||
<service
|
||||
android:name=".jobs.NContentObserverJob"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" >
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".authentication.AccountAuthenticatorService"
|
||||
android:exported="true" >
|
||||
|
@ -131,7 +136,7 @@
|
|||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
<service android:name=".services.observer.SyncedFolderObserverService"/>
|
||||
|
||||
<service
|
||||
android:name=".syncadapter.FileSyncService"
|
||||
android:exported="true" >
|
||||
|
@ -160,7 +165,7 @@
|
|||
android:label="@string/search_users_and_groups_hint" />
|
||||
|
||||
<provider
|
||||
android:name="org.nextcloud.providers.DocumentsStorageProvider"
|
||||
android:name=".providers.DocumentsStorageProvider"
|
||||
android:authorities="@string/document_provider_authority"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
|
@ -226,43 +231,12 @@
|
|||
<activity android:name=".ui.activity.UploadListActivity" />
|
||||
<activity android:name=".ui.activity.WhatsNewActivity"
|
||||
android:theme="@style/Theme.ownCloud.noActionBar.Login" />
|
||||
|
||||
<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"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".files.InstantUploadBroadcastReceiver">
|
||||
<intent-filter>
|
||||
|
||||
<!-- unofficially supported by many Android phones but not by HTC devices: -->
|
||||
<action android:name="com.android.camera.NEW_PICTURE" />
|
||||
<!-- officially supported since Android 4.0 (SDK 14, works even for HTC devices): -->
|
||||
<action android:name="android.hardware.action.NEW_PICTURE" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.action.NEW_VIDEO" />
|
||||
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".files.BootupBroadcastReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".services.ShutdownReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWEROFF" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
<service android:name=".services.observer.FileObserverService" />
|
||||
|
|
|
@ -35,7 +35,6 @@ public abstract class SectionedRecyclerViewAdapter<VH extends RecyclerView.ViewH
|
|||
|
||||
private final ArrayMap<Integer, Integer> mHeaderLocationMap;
|
||||
private GridLayoutManager mLayoutManager;
|
||||
private ArrayMap<Integer, Integer> mSpanMap;
|
||||
private boolean mShowHeadersForEmptySections;
|
||||
|
||||
public SectionedRecyclerViewAdapter() {
|
||||
|
|
|
@ -19,34 +19,42 @@
|
|||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.support.multidex.MultiDexApplication;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.evernote.android.job.JobManager;
|
||||
import com.owncloud.android.authentication.PassCodeManager;
|
||||
import com.owncloud.android.datamodel.MediaFolder;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.MediaProvider;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.db.PreferenceManager;
|
||||
import com.owncloud.android.jobs.NCJobCreator;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.NCJobCreator;
|
||||
import com.owncloud.android.services.observer.SyncedFolderObserverService;
|
||||
import com.owncloud.android.ui.activity.Preferences;
|
||||
import com.owncloud.android.ui.activity.SyncedFoldersActivity;
|
||||
import com.owncloud.android.ui.activity.WhatsNewActivity;
|
||||
import com.owncloud.android.utils.AnalyticsUtils;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.PermissionUtil;
|
||||
import com.owncloud.android.utils.ReceiversHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -79,8 +87,6 @@ public class MainApp extends MultiDexApplication {
|
|||
|
||||
private static boolean mOnlyOnDevice = false;
|
||||
|
||||
private static SyncedFolderObserverService mObserverService;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private boolean mBound;
|
||||
|
||||
|
@ -118,14 +124,26 @@ public class MainApp extends MultiDexApplication {
|
|||
Log_OC.d("Debug", "start logging");
|
||||
}
|
||||
|
||||
updateToAutoUpload();
|
||||
cleanOldEntries();
|
||||
updateAutoUploadEntries();
|
||||
|
||||
Log_OC.d("SyncedFolderObserverService", "Start service SyncedFolderObserverService");
|
||||
Intent i = new Intent(this, SyncedFolderObserverService.class);
|
||||
startService(i);
|
||||
bindService(i, syncedFolderObserverServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
if (PermissionUtil.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
splitOutAutoUploadEntries();
|
||||
} else {
|
||||
PreferenceManager.setAutoUploadSplitEntries(this, true);
|
||||
}
|
||||
|
||||
initiateExistingAutoUploadEntries();
|
||||
|
||||
FilesSyncHelper.scheduleFilesSyncIfNeeded();
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
|
||||
ReceiversHelper.registerNetworkChangeReceiver();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
ReceiversHelper.registerPowerChangeReceiver();
|
||||
}
|
||||
|
||||
// register global protection with pass code
|
||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||
|
@ -245,10 +263,6 @@ public class MainApp extends MultiDexApplication {
|
|||
return mOnlyOnDevice;
|
||||
}
|
||||
|
||||
public static SyncedFolderObserverService getSyncedFolderObserverService() {
|
||||
return mObserverService;
|
||||
}
|
||||
|
||||
// user agent
|
||||
public static String getUserAgent() {
|
||||
String appString = getAppContext().getResources().getString(R.string.user_agent);
|
||||
|
@ -271,6 +285,48 @@ public class MainApp extends MultiDexApplication {
|
|||
return userAgent;
|
||||
}
|
||||
|
||||
private void updateToAutoUpload() {
|
||||
if (PreferenceManager.instantPictureUploadEnabled(this) ||
|
||||
PreferenceManager.instantPictureUploadEnabled(this)) {
|
||||
|
||||
// remove legacy shared preferences
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
|
||||
editor.remove("instant_uploading")
|
||||
.remove("instant_video_uploading")
|
||||
.remove("instant_upload_path")
|
||||
.remove("instant_upload_path_use_subfolders")
|
||||
.remove("instant_upload_on_wifi")
|
||||
.remove("instant_upload_on_charging")
|
||||
.remove("instant_video_upload_path")
|
||||
.remove("instant_video_upload_path_use_subfolders")
|
||||
.remove("instant_video_upload_on_wifi")
|
||||
.remove("instant_video_uploading")
|
||||
.remove("instant_video_upload_on_charging")
|
||||
.remove("prefs_instant_behaviour").apply();
|
||||
|
||||
// show info pop-up
|
||||
new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog)
|
||||
.setTitle(R.string.drawer_synced_folders)
|
||||
.setMessage(R.string.synced_folders_new_info)
|
||||
.setPositiveButton(R.string.drawer_open, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// show Auto Upload
|
||||
Intent folderSyncIntent = new Intent(getApplicationContext(),
|
||||
SyncedFoldersActivity.class);
|
||||
dialog.dismiss();
|
||||
startActivity(folderSyncIntent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.drawer_close, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setIcon(R.drawable.nav_synced_folders)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAutoUploadEntries() {
|
||||
// updates entries to reflect their true paths
|
||||
if (!PreferenceManager.getAutoUploadPathsUpdate(this)) {
|
||||
|
@ -280,6 +336,77 @@ public class MainApp extends MultiDexApplication {
|
|||
}
|
||||
}
|
||||
|
||||
private void splitOutAutoUploadEntries() {
|
||||
if (!PreferenceManager.getAutoUploadSplitEntries(this)) {
|
||||
// magic to split out existing synced folders in two when needed
|
||||
// otherwise, we migrate them to their proper type (image or video)
|
||||
Log_OC.i(TAG, "Migrate synced_folders records for image/video split");
|
||||
ContentResolver contentResolver = this.getContentResolver();
|
||||
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver);
|
||||
|
||||
final List<MediaFolder> imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1, null);
|
||||
final List<MediaFolder> videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1);
|
||||
|
||||
ArrayList<Long> idsToDelete = new ArrayList<>();
|
||||
List<SyncedFolder> syncedFolders = syncedFolderProvider.getSyncedFolders();
|
||||
long primaryKey;
|
||||
SyncedFolder newSyncedFolder;
|
||||
for (SyncedFolder syncedFolder : syncedFolders) {
|
||||
idsToDelete.add(syncedFolder.getId());
|
||||
Log_OC.i(TAG, "Migration check for synced_folders record: "
|
||||
+ syncedFolder.getId() + " - " + syncedFolder.getLocalPath());
|
||||
|
||||
for (int i = 0; i < imageMediaFolders.size(); i++) {
|
||||
if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) {
|
||||
newSyncedFolder = (SyncedFolder) syncedFolder.clone();
|
||||
newSyncedFolder.setType(MediaFolderType.IMAGE);
|
||||
primaryKey = syncedFolderProvider.storeSyncedFolder(newSyncedFolder);
|
||||
Log_OC.i(TAG, "Migrated image synced_folders record: "
|
||||
+ primaryKey + " - " + newSyncedFolder.getLocalPath());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < videoMediaFolders.size(); j++) {
|
||||
if (videoMediaFolders.get(j).absolutePath.equals(syncedFolder.getLocalPath())) {
|
||||
newSyncedFolder = (SyncedFolder) syncedFolder.clone();
|
||||
newSyncedFolder.setType(MediaFolderType.VIDEO);
|
||||
primaryKey = syncedFolderProvider.storeSyncedFolder(newSyncedFolder);
|
||||
Log_OC.i(TAG, "Migrated video synced_folders record: "
|
||||
+ primaryKey + " - " + newSyncedFolder.getLocalPath());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (long id : idsToDelete) {
|
||||
Log_OC.i(TAG, "Removing legacy synced_folders record: " + id);
|
||||
syncedFolderProvider.deleteSyncedFolder(id);
|
||||
}
|
||||
|
||||
PreferenceManager.setAutoUploadSplitEntries(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initiateExistingAutoUploadEntries() {
|
||||
new Thread(() -> {
|
||||
if (!PreferenceManager.getAutoUploadInit(getAppContext())) {
|
||||
SyncedFolderProvider syncedFolderProvider =
|
||||
new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if (syncedFolder.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder);
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceManager.setAutoUploadInit(getAppContext(), true);
|
||||
}
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void cleanOldEntries() {
|
||||
// previous versions of application created broken entries in the SyncedFolderProvider
|
||||
// database, and this cleans all that and leaves 1 (newest) entry per synced folder
|
||||
|
@ -302,9 +429,7 @@ public class MainApp extends MultiDexApplication {
|
|||
}
|
||||
}
|
||||
|
||||
for (Long idValue : syncedFolders.values()) {
|
||||
ids.add(idValue);
|
||||
}
|
||||
ids.addAll(syncedFolders.values());
|
||||
|
||||
if (ids.size() > 0) {
|
||||
syncedFolderProvider.deleteSyncedFoldersNotInList(mContext, ids);
|
||||
|
@ -313,24 +438,4 @@ public class MainApp extends MultiDexApplication {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines callbacks for service binding, passed to bindService()
|
||||
*/
|
||||
private ServiceConnection syncedFolderObserverServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
SyncedFolderObserverService.SyncedFolderObserverBinder binder =
|
||||
(SyncedFolderObserverService.SyncedFolderObserverBinder) service;
|
||||
mObserverService = binder.getService();
|
||||
mBound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
mBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.view.WindowManager;
|
|||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.ui.activity.FingerprintActivity;
|
||||
import com.owncloud.android.ui.activity.PassCodeActivity;
|
||||
import com.owncloud.android.ui.activity.Preferences;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -131,6 +132,7 @@ public class PassCodeManager {
|
|||
|
||||
private boolean fingerprintIsEnabled() {
|
||||
SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext());
|
||||
return (appPrefs.getBoolean(FingerprintActivity.PREFERENCE_USE_FINGERPRINT, false));
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,36 +47,43 @@ public class ArbitraryDataProvider {
|
|||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public int deleteKeyForAccount(Account account, String key) {
|
||||
int result = contentResolver.delete(
|
||||
public int deleteKeyForAccount(String account, String key) {
|
||||
return contentResolver.delete(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? AND " +
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + "= ?",
|
||||
new String[]{account.name, key}
|
||||
new String[]{account, key}
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
public int deleteForKeyWhereAccountNotIn(ArrayList<String> accounts, String key) {
|
||||
return contentResolver.delete(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " NOT IN (?) AND " +
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + "= ?",
|
||||
new String[]{String.valueOf(accounts), key}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public void storeOrUpdateKeyValue(Account account, String key, String newValue) {
|
||||
ArbitraryDataSet data = getArbitraryDataSet(account, key);
|
||||
public void storeOrUpdateKeyValue(String accountName, String key, String newValue) {
|
||||
ArbitraryDataSet data = getArbitraryDataSet(accountName, key);
|
||||
if (data == null) {
|
||||
Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + account.name + " key: " + key
|
||||
Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + accountName + " key: " + key
|
||||
+ " value: " + newValue);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, account.name);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, accountName);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, key);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, newValue);
|
||||
|
||||
Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA, cv);
|
||||
|
||||
if (result == null) {
|
||||
Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + account.name + " key: " + key
|
||||
Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + accountName + " key: " + key
|
||||
+ " value: " + newValue);
|
||||
}
|
||||
} else {
|
||||
Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + account.name + " key: " + key
|
||||
Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + accountName + " key: " + key
|
||||
+ " value: " + newValue);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, data.getCloudId());
|
||||
|
@ -91,7 +98,7 @@ public class ArbitraryDataProvider {
|
|||
);
|
||||
|
||||
if (result == 0) {
|
||||
Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + account.name + " key: " + key
|
||||
Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + accountName + " key: " + key
|
||||
+ " value: " + newValue);
|
||||
}
|
||||
}
|
||||
|
@ -146,38 +153,6 @@ public class ArbitraryDataProvider {
|
|||
return getIntegerValue(account.name, key);
|
||||
}
|
||||
|
||||
private ArrayList<String> getValues(Account account, String key) {
|
||||
Cursor cursor = contentResolver.query(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
|
||||
null,
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
|
||||
new String[]{account.name, key},
|
||||
null
|
||||
);
|
||||
|
||||
if (cursor != null) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
String value = cursor.getString(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
|
||||
if (value == null) {
|
||||
Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
|
||||
} else {
|
||||
list.add(value);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
return list;
|
||||
} else {
|
||||
Log_OC.e(TAG, "DB error restoring arbitrary values.");
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stored value as string or empty string
|
||||
* @return string if value found or empty string
|
||||
|
@ -215,13 +190,13 @@ public class ArbitraryDataProvider {
|
|||
return "";
|
||||
}
|
||||
|
||||
private ArbitraryDataSet getArbitraryDataSet(Account account, String key) {
|
||||
private ArbitraryDataSet getArbitraryDataSet(String accountName, String key) {
|
||||
Cursor cursor = contentResolver.query(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
|
||||
null,
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
|
||||
new String[]{account.name, key},
|
||||
new String[]{accountName, key},
|
||||
null
|
||||
);
|
||||
|
||||
|
|
|
@ -593,7 +593,6 @@ public class FileDataStorageManager {
|
|||
if (localFile.isDirectory()) {
|
||||
success &= removeLocalFolder(localFile);
|
||||
} else {
|
||||
String path = localFile.getAbsolutePath();
|
||||
success &= localFile.delete();
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +601,6 @@ public class FileDataStorageManager {
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates database and file system for a file or folder that was moved to a different location.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
/*
|
||||
Model for filesystem data from the database
|
||||
*/
|
||||
public class FileSystemDataSet {
|
||||
|
||||
private int id;
|
||||
private String localPath;
|
||||
private long modifiedAt;
|
||||
private boolean isFolder;
|
||||
private boolean isSentForUpload;
|
||||
private long foundAt;
|
||||
private long syncedFolderId;
|
||||
|
||||
public FileSystemDataSet() {
|
||||
}
|
||||
|
||||
public FileSystemDataSet(int id, String localPath, long modifiedAt, boolean isFolder,
|
||||
boolean isSentForUpload, long foundAt, long syncedFolderId) {
|
||||
this.id = id;
|
||||
this.localPath = localPath;
|
||||
this.modifiedAt = modifiedAt;
|
||||
this.isFolder = isFolder;
|
||||
this.isSentForUpload = isSentForUpload;
|
||||
this.foundAt = foundAt;
|
||||
this.syncedFolderId = syncedFolderId;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLocalPath() {
|
||||
return localPath;
|
||||
}
|
||||
|
||||
public void setLocalPath(String localPath) {
|
||||
this.localPath = localPath;
|
||||
}
|
||||
|
||||
public long getModifiedAt() {
|
||||
return modifiedAt;
|
||||
}
|
||||
|
||||
public void setModifiedAt(long modifiedAt) {
|
||||
this.modifiedAt = modifiedAt;
|
||||
}
|
||||
|
||||
public boolean isFolder() {
|
||||
return isFolder;
|
||||
}
|
||||
|
||||
public void setFolder(boolean folder) {
|
||||
isFolder = folder;
|
||||
}
|
||||
|
||||
public long getFoundAt() {
|
||||
return foundAt;
|
||||
}
|
||||
|
||||
public void setFoundAt(long foundAt) {
|
||||
this.foundAt = foundAt;
|
||||
}
|
||||
|
||||
public boolean isSentForUpload() {
|
||||
return isSentForUpload;
|
||||
}
|
||||
|
||||
public void setSentForUpload(boolean sentForUpload) {
|
||||
isSentForUpload = sentForUpload;
|
||||
}
|
||||
|
||||
public long getSyncedFolderId() {
|
||||
return syncedFolderId;
|
||||
}
|
||||
|
||||
public void setSyncedFolderId(long syncedFolderId) {
|
||||
this.syncedFolderId = syncedFolderId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero 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.ProviderMeta;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provider for stored filesystem data.
|
||||
*/
|
||||
public class FilesystemDataProvider {
|
||||
|
||||
static private final String TAG = FilesystemDataProvider.class.getSimpleName();
|
||||
|
||||
private ContentResolver contentResolver;
|
||||
|
||||
public FilesystemDataProvider(ContentResolver contentResolver) {
|
||||
if (contentResolver == null) {
|
||||
throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
|
||||
}
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public void updateFilesystemFileAsSentForUpload(String path, String syncedFolderId) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 1);
|
||||
|
||||
contentResolver.update(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM,
|
||||
cv,
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?",
|
||||
new String[]{path, syncedFolderId}
|
||||
);
|
||||
}
|
||||
|
||||
public Set<String> getFilesForUpload(String localPath, String syncedFolderId) {
|
||||
Set<String> localPathsToUpload = new HashSet<>();
|
||||
|
||||
String likeParam = localPath + "%";
|
||||
|
||||
Cursor cursor = contentResolver.query(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM,
|
||||
null,
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ? and " +
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " = ?",
|
||||
new String[]{likeParam, syncedFolderId, "0", "0"},
|
||||
null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
String value = cursor.getString(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH));
|
||||
if (value == null) {
|
||||
Log_OC.e(TAG, "Cannot get local path");
|
||||
} else {
|
||||
localPathsToUpload.add(value);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return localPathsToUpload;
|
||||
}
|
||||
|
||||
public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) {
|
||||
|
||||
FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder);
|
||||
|
||||
int isFolderValue = 0;
|
||||
if (isFolder) {
|
||||
isFolderValue = 1;
|
||||
}
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt);
|
||||
|
||||
if (data == null) {
|
||||
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, false);
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId());
|
||||
|
||||
Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv);
|
||||
|
||||
if (result == null) {
|
||||
Log_OC.v(TAG, "Failed to insert filesystem data with local path: " + localPath);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (data.getModifiedAt() != modifiedAt) {
|
||||
cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0);
|
||||
}
|
||||
|
||||
|
||||
int result = contentResolver.update(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM,
|
||||
cv,
|
||||
ProviderMeta.ProviderTableMeta._ID + "=?",
|
||||
new String[]{String.valueOf(data.getId())}
|
||||
);
|
||||
|
||||
if (result == 0) {
|
||||
Log_OC.v(TAG, "Failed to update filesystem data with local path: " + localPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFolder syncedFolder) {
|
||||
|
||||
Cursor cursor = contentResolver.query(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM,
|
||||
null,
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ? and " +
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ?",
|
||||
new String[]{localPathParam, Long.toString(syncedFolder.getId())},
|
||||
null
|
||||
);
|
||||
|
||||
FileSystemDataSet dataSet = null;
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
int id = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
|
||||
String localPath = cursor.getString(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH));
|
||||
long modifiedAt = cursor.getLong(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED));
|
||||
boolean isFolder = false;
|
||||
if (cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0) {
|
||||
isFolder = true;
|
||||
}
|
||||
long foundAt = cursor.getLong(cursor.getColumnIndex(ProviderMeta.
|
||||
ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY));
|
||||
|
||||
boolean isSentForUpload = false;
|
||||
if (cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) {
|
||||
isSentForUpload = true;
|
||||
}
|
||||
|
||||
if (id == -1) {
|
||||
Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
|
||||
} else {
|
||||
dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt,
|
||||
syncedFolder.getId());
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
} else {
|
||||
Log_OC.e(TAG, "DB error restoring arbitrary values.");
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
}
|
||||
}
|
|
@ -4,17 +4,17 @@
|
|||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2016 Andy Scherzinger
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* <p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
* <p>
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
@ -38,4 +38,7 @@ public class MediaFolder {
|
|||
|
||||
/** total number of files in the media folder. */
|
||||
public long numberOfFiles;
|
||||
|
||||
/** type of media folder. */
|
||||
public MediaFolderType type;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2017 Andy Scherzinger
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
/**
|
||||
* Types of media folder.
|
||||
*/
|
||||
public enum MediaFolderType {
|
||||
CUSTOM(0),
|
||||
IMAGE(1),
|
||||
VIDEO(2);
|
||||
|
||||
private Integer id;
|
||||
|
||||
private static SparseArray<MediaFolderType> reverseMap = new SparseArray<>(3);
|
||||
|
||||
static {
|
||||
reverseMap.put(CUSTOM.getId(), CUSTOM);
|
||||
reverseMap.put(IMAGE.getId(), IMAGE);
|
||||
reverseMap.put(VIDEO.getId(), VIDEO);
|
||||
}
|
||||
|
||||
MediaFolderType(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static MediaFolderType getById(Integer id) {
|
||||
return reverseMap.get(id);
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -40,6 +40,8 @@ import java.io.File;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Media queries to gain access to media lists for the device.
|
||||
*/
|
||||
|
@ -47,12 +49,15 @@ public class MediaProvider {
|
|||
private static final String TAG = MediaProvider.class.getSimpleName();
|
||||
|
||||
// fixed query parameters
|
||||
private static final Uri MEDIA_URI = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
private static final Uri IMAGES_MEDIA_URI = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
private static final String[] FILE_PROJECTION = new String[]{MediaStore.MediaColumns.DATA};
|
||||
private static final String FILE_SELECTION = MediaStore.Images.Media.BUCKET_ID + "=";
|
||||
private static final String[] FOLDER_PROJECTION = { "Distinct " + MediaStore.Images.Media.BUCKET_ID,
|
||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME };
|
||||
private static final String FOLDER_SORT_ORDER = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " ASC";
|
||||
private static final String IMAGES_FILE_SELECTION = MediaStore.Images.Media.BUCKET_ID + "=";
|
||||
private static final String[] IMAGES_FOLDER_PROJECTION = {"Distinct " + MediaStore.Images.Media.BUCKET_ID,
|
||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
|
||||
private static final String IMAGES_FOLDER_SORT_ORDER = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " ASC";
|
||||
|
||||
private static final String[] VIDEOS_FOLDER_PROJECTION = {"Distinct " + MediaStore.Video.Media.BUCKET_ID,
|
||||
MediaStore.Video.Media.BUCKET_DISPLAY_NAME};
|
||||
|
||||
/**
|
||||
* Getting All Images Paths.
|
||||
|
@ -61,11 +66,105 @@ public class MediaProvider {
|
|||
* @param itemLimit the number of media items (usually images) to be returned per media folder.
|
||||
* @return list with media folders
|
||||
*/
|
||||
public static List<MediaFolder> getMediaFolders(ContentResolver contentResolver, int itemLimit,
|
||||
final Activity activity) {
|
||||
public static List<MediaFolder> getImageFolders(ContentResolver contentResolver, int itemLimit,
|
||||
@Nullable final Activity activity) {
|
||||
// check permissions
|
||||
if (!PermissionUtil.checkSelfPermission(activity.getApplicationContext(),
|
||||
checkPermissions(activity);
|
||||
|
||||
|
||||
// query media/image folders
|
||||
Cursor cursorFolders;
|
||||
if (activity != null && PermissionUtil.checkSelfPermission(activity.getApplicationContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
cursorFolders = contentResolver.query(
|
||||
IMAGES_MEDIA_URI,
|
||||
IMAGES_FOLDER_PROJECTION,
|
||||
null,
|
||||
null,
|
||||
IMAGES_FOLDER_SORT_ORDER
|
||||
);
|
||||
} else {
|
||||
cursorFolders = contentResolver.query(
|
||||
IMAGES_MEDIA_URI,
|
||||
IMAGES_FOLDER_PROJECTION,
|
||||
null,
|
||||
null,
|
||||
IMAGES_FOLDER_SORT_ORDER
|
||||
);
|
||||
}
|
||||
List<MediaFolder> mediaFolders = new ArrayList<>();
|
||||
String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder();
|
||||
|
||||
if (cursorFolders != null) {
|
||||
String folderName;
|
||||
String fileSortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC LIMIT " + itemLimit;
|
||||
Cursor cursorImages;
|
||||
|
||||
while (cursorFolders.moveToNext()) {
|
||||
String folderId = cursorFolders.getString(cursorFolders.getColumnIndex(MediaStore.Images.Media
|
||||
.BUCKET_ID));
|
||||
|
||||
MediaFolder mediaFolder = new MediaFolder();
|
||||
folderName = cursorFolders.getString(cursorFolders.getColumnIndex(
|
||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
|
||||
mediaFolder.type = MediaFolderType.IMAGE;
|
||||
mediaFolder.folderName = folderName;
|
||||
mediaFolder.filePaths = new ArrayList<>();
|
||||
|
||||
// query images
|
||||
cursorImages = contentResolver.query(
|
||||
IMAGES_MEDIA_URI,
|
||||
FILE_PROJECTION,
|
||||
IMAGES_FILE_SELECTION + folderId,
|
||||
null,
|
||||
fileSortOrder
|
||||
);
|
||||
Log.d(TAG, "Reading images for " + mediaFolder.folderName);
|
||||
|
||||
if (cursorImages != null) {
|
||||
String filePath;
|
||||
|
||||
while (cursorImages.moveToNext()) {
|
||||
filePath = cursorImages.getString(cursorImages.getColumnIndexOrThrow(
|
||||
MediaStore.MediaColumns.DATA));
|
||||
|
||||
if (filePath != null) {
|
||||
mediaFolder.filePaths.add(filePath);
|
||||
mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf("/"));
|
||||
}
|
||||
}
|
||||
cursorImages.close();
|
||||
|
||||
// only do further work if folder is not within the Nextcloud app itself
|
||||
if (!mediaFolder.absolutePath.startsWith(dataPath)) {
|
||||
|
||||
// count images
|
||||
Cursor count = contentResolver.query(
|
||||
IMAGES_MEDIA_URI,
|
||||
FILE_PROJECTION,
|
||||
IMAGES_FILE_SELECTION + folderId,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (count != null) {
|
||||
mediaFolder.numberOfFiles = count.getCount();
|
||||
count.close();
|
||||
}
|
||||
|
||||
mediaFolders.add(mediaFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
cursorFolders.close();
|
||||
}
|
||||
|
||||
return mediaFolders;
|
||||
}
|
||||
|
||||
private static void checkPermissions(@Nullable Activity activity) {
|
||||
if (activity != null &&
|
||||
!PermissionUtil.checkSelfPermission(activity.getApplicationContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
// Check if we should show an explanation
|
||||
if (PermissionUtil.shouldShowRequestPermissionRationale(activity,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
|
@ -87,49 +186,46 @@ public class MediaProvider {
|
|||
PermissionUtil.requestWriteExternalStoreagePermission(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// query media/image folders
|
||||
Cursor cursorFolders = null;
|
||||
if (PermissionUtil.checkSelfPermission(activity.getApplicationContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
cursorFolders = contentResolver.query(MEDIA_URI, FOLDER_PROJECTION, null, null, FOLDER_SORT_ORDER);
|
||||
}
|
||||
public static List<MediaFolder> getVideoFolders(ContentResolver contentResolver, int itemLimit) {
|
||||
Cursor cursorFolders = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
VIDEOS_FOLDER_PROJECTION, null, null, null);
|
||||
List<MediaFolder> mediaFolders = new ArrayList<>();
|
||||
String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder();
|
||||
|
||||
if (cursorFolders != null) {
|
||||
String folderName;
|
||||
String fileSortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC LIMIT " + itemLimit;
|
||||
String fileSortOrder = MediaStore.Video.Media.DATE_TAKEN + " DESC LIMIT " + itemLimit;
|
||||
Cursor cursorImages;
|
||||
|
||||
while (cursorFolders.moveToNext()) {
|
||||
String folderId = cursorFolders.getString(cursorFolders.getColumnIndex(MediaStore.Images.Media
|
||||
String folderId = cursorFolders.getString(cursorFolders.getColumnIndex(MediaStore.Video.Media
|
||||
.BUCKET_ID));
|
||||
|
||||
MediaFolder mediaFolder = new MediaFolder();
|
||||
folderName = cursorFolders.getString(cursorFolders.getColumnIndex(
|
||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
|
||||
MediaStore.Video.Media.BUCKET_DISPLAY_NAME));
|
||||
mediaFolder.type = MediaFolderType.VIDEO;
|
||||
mediaFolder.folderName = folderName;
|
||||
mediaFolder.filePaths = new ArrayList<>();
|
||||
|
||||
// query images
|
||||
cursorImages = contentResolver.query(MEDIA_URI, FILE_PROJECTION, FILE_SELECTION + folderId, null,
|
||||
cursorImages = contentResolver.query(
|
||||
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
FILE_PROJECTION,
|
||||
MediaStore.Video.Media.BUCKET_ID + "=" + folderId,
|
||||
null,
|
||||
fileSortOrder);
|
||||
Log.d(TAG, "Reading images for " + mediaFolder.folderName);
|
||||
Log.d(TAG, "Reading videos for " + mediaFolder.folderName);
|
||||
|
||||
if (cursorImages != null) {
|
||||
String filePath;
|
||||
int failedImages = 0;
|
||||
while (cursorImages.moveToNext()) {
|
||||
filePath = cursorImages.getString(cursorImages.getColumnIndexOrThrow(
|
||||
MediaStore.MediaColumns.DATA));
|
||||
|
||||
if (filePath != null) {
|
||||
mediaFolder.filePaths.add(filePath);
|
||||
mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf("/"));
|
||||
} else {
|
||||
failedImages++;
|
||||
}
|
||||
mediaFolder.filePaths.add(filePath);
|
||||
mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf("/"));
|
||||
}
|
||||
cursorImages.close();
|
||||
|
||||
|
@ -138,14 +234,14 @@ public class MediaProvider {
|
|||
|
||||
// count images
|
||||
Cursor count = contentResolver.query(
|
||||
MEDIA_URI,
|
||||
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
FILE_PROJECTION,
|
||||
FILE_SELECTION + folderId,
|
||||
MediaStore.Video.Media.BUCKET_ID + "=" + folderId,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (count != null) {
|
||||
mediaFolder.numberOfFiles = count.getCount() - failedImages;
|
||||
mediaFolder.numberOfFiles = count.getCount();
|
||||
count.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.io.Serializable;
|
|||
/**
|
||||
* Synced folder entity containing all information per synced folder.
|
||||
*/
|
||||
public class SyncedFolder implements Serializable {
|
||||
public class SyncedFolder implements Serializable, Cloneable {
|
||||
public static final long UNPERSISTED_ID = Long.MIN_VALUE;
|
||||
private static final long serialVersionUID = -793476118299906429L;
|
||||
private long id = UNPERSISTED_ID;
|
||||
|
@ -38,6 +38,7 @@ public class SyncedFolder implements Serializable {
|
|||
private String account;
|
||||
private Integer uploadAction;
|
||||
private boolean enabled;
|
||||
private MediaFolderType type;
|
||||
|
||||
/**
|
||||
* constructor for already persisted entity.
|
||||
|
@ -51,9 +52,11 @@ public class SyncedFolder implements Serializable {
|
|||
* @param account the account owning the synced folder
|
||||
* @param uploadAction the action to be done after the upload
|
||||
* @param enabled flag if synced folder config is active
|
||||
* @param type the type of the folder
|
||||
*/
|
||||
public SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled) {
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
|
||||
MediaFolderType type) {
|
||||
this.id = id;
|
||||
this.localPath = localPath;
|
||||
this.remotePath = remotePath;
|
||||
|
@ -63,6 +66,7 @@ public class SyncedFolder implements Serializable {
|
|||
this.account = account;
|
||||
this.uploadAction = uploadAction;
|
||||
this.enabled = enabled;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,9 +80,11 @@ public class SyncedFolder implements Serializable {
|
|||
* @param account the account owning the synced folder
|
||||
* @param uploadAction the action to be done after the upload
|
||||
* @param enabled flag if synced folder config is active
|
||||
* @param type the type of the folder
|
||||
*/
|
||||
public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled) {
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
|
||||
MediaFolderType type) {
|
||||
this.localPath = localPath;
|
||||
this.remotePath = remotePath;
|
||||
this.wifiOnly = wifiOnly;
|
||||
|
@ -87,6 +93,15 @@ public class SyncedFolder implements Serializable {
|
|||
this.account = account;
|
||||
this.uploadAction = uploadAction;
|
||||
this.enabled = enabled;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
|
@ -160,4 +175,12 @@ public class SyncedFolder implements Serializable {
|
|||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public MediaFolderType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(MediaFolderType type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -47,11 +47,13 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
* @param filePaths the UI info for the file path
|
||||
* @param folderName the UI info for the folder's name
|
||||
* @param numberOfFiles the UI info for number of files within the folder
|
||||
* @param type the type of the folder
|
||||
*/
|
||||
public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
|
||||
List<String> filePaths, String folderName, long numberOfFiles) {
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled);
|
||||
List<String> filePaths, String folderName, long numberOfFiles, MediaFolderType type)
|
||||
{
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type);
|
||||
this.filePaths = filePaths;
|
||||
this.folderName = folderName;
|
||||
this.numberOfFiles = numberOfFiles;
|
||||
|
@ -59,12 +61,11 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
|
|||
|
||||
public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
|
||||
Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
|
||||
String folderName) {
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled);
|
||||
String folderName, MediaFolderType type) {
|
||||
super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type);
|
||||
this.folderName = folderName;
|
||||
}
|
||||
|
||||
|
||||
public List<String> getFilePaths() {
|
||||
return filePaths;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* Copyright (C) 2016 Andy Scherzinger
|
||||
* Copyright (C) 2016 Nextcloud.
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2016 Andy Scherzinger
|
||||
* Copyright (C) 2016 Nextcloud.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
|
@ -27,7 +28,6 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.db.PreferenceManager;
|
||||
import com.owncloud.android.db.ProviderMeta;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
@ -58,12 +58,12 @@ public class SyncedFolderProvider extends Observable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stores an media folder sync object in database.
|
||||
* Stores a synced folder object in database.
|
||||
*
|
||||
* @param syncedFolder synced folder to store
|
||||
* @return synced folder id, -1 if the insert process fails.
|
||||
*/
|
||||
public long storeFolderSync(SyncedFolder syncedFolder) {
|
||||
public long storeSyncedFolder(SyncedFolder syncedFolder) {
|
||||
Log_OC.v(TAG, "Inserting " + syncedFolder.getLocalPath() + " with enabled=" + syncedFolder.isEnabled());
|
||||
|
||||
ContentValues cv = createContentValuesFromSyncedFolder(syncedFolder);
|
||||
|
@ -71,7 +71,6 @@ public class SyncedFolderProvider extends Observable {
|
|||
Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, cv);
|
||||
|
||||
if (result != null) {
|
||||
notifyFolderSyncObservers(syncedFolder);
|
||||
return Long.parseLong(result.getPathSegments().get(1));
|
||||
} else {
|
||||
Log_OC.e(TAG, "Failed to insert item " + syncedFolder.getLocalPath() + " into folder sync db.");
|
||||
|
@ -118,12 +117,12 @@ public class SyncedFolderProvider extends Observable {
|
|||
/**
|
||||
* Update upload status of file uniquely referenced by id.
|
||||
*
|
||||
* @param id folder sync id.
|
||||
* @param id synced folder id.
|
||||
* @param enabled new status.
|
||||
* @return the number of rows updated.
|
||||
*/
|
||||
public int updateFolderSyncEnabled(long id, Boolean enabled) {
|
||||
Log_OC.v(TAG, "Storing sync folder id" + id + " with enabled=" + enabled);
|
||||
public int updateSyncedFolderEnabled(long id, Boolean enabled) {
|
||||
Log_OC.v(TAG, "Storing synced folder id" + id + " with enabled=" + enabled);
|
||||
|
||||
int result = 0;
|
||||
Cursor cursor = mContentResolver.query(
|
||||
|
@ -187,7 +186,6 @@ public class SyncedFolderProvider extends Observable {
|
|||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,15 +209,12 @@ public class SyncedFolderProvider extends Observable {
|
|||
*
|
||||
* @param id for the synced folder.
|
||||
*/
|
||||
|
||||
private int deleteSyncFolderWithId(long id) {
|
||||
int result = mContentResolver.delete(
|
||||
return mContentResolver.delete(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
||||
ProviderMeta.ProviderTableMeta._ID + " = ?",
|
||||
new String[]{String.valueOf(id)}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -274,6 +269,17 @@ public class SyncedFolderProvider extends Observable {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete record of synchronized folder with the given id.
|
||||
*/
|
||||
public int deleteSyncedFolder(long id) {
|
||||
return mContentResolver.delete(
|
||||
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
||||
ProviderMeta.ProviderTableMeta._ID + " = ?",
|
||||
new String[]{String.valueOf(id)}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* update given synced folder.
|
||||
*
|
||||
|
@ -292,10 +298,6 @@ public class SyncedFolderProvider extends Observable {
|
|||
new String[]{String.valueOf(syncedFolder.getId())}
|
||||
);
|
||||
|
||||
if (result > 0) {
|
||||
notifyFolderSyncObservers(syncedFolder);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -325,9 +327,11 @@ public class SyncedFolderProvider extends Observable {
|
|||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
|
||||
Boolean enabled = cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
|
||||
MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex(
|
||||
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)));
|
||||
|
||||
syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
|
||||
accountName, uploadAction, enabled);
|
||||
accountName, uploadAction, enabled, type);
|
||||
}
|
||||
return syncedFolder;
|
||||
}
|
||||
|
@ -349,18 +353,8 @@ public class SyncedFolderProvider extends Observable {
|
|||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId());
|
||||
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform all observers about data change.
|
||||
*
|
||||
* @param syncedFolder changed, synchronized folder
|
||||
*/
|
||||
private void notifyFolderSyncObservers(SyncedFolder syncedFolder) {
|
||||
if (syncedFolder != null) {
|
||||
MainApp.getSyncedFolderObserverService().restartObserver(syncedFolder);
|
||||
Log_OC.d(TAG, "notifying folder sync data observers for changed/added: " + syncedFolder.getLocalPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import android.graphics.Canvas;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.ThumbnailUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
@ -418,6 +419,7 @@ public class ThumbnailsCacheManager {
|
|||
}
|
||||
|
||||
public static class MediaThumbnailGenerationTask extends AsyncTask<Object, Void, Bitmap> {
|
||||
private enum Type {IMAGE, VIDEO}
|
||||
private final WeakReference<ImageView> mImageViewReference;
|
||||
private File mFile;
|
||||
private String mImageKey = null;
|
||||
|
@ -439,7 +441,9 @@ public class ThumbnailsCacheManager {
|
|||
}
|
||||
|
||||
if (MimeTypeUtil.isImage(mFile)) {
|
||||
thumbnail = doFileInBackground(mFile);
|
||||
thumbnail = doFileInBackground(mFile, Type.IMAGE);
|
||||
} else if (MimeTypeUtil.isVideo(mFile)) {
|
||||
thumbnail = doFileInBackground(mFile, Type.VIDEO);
|
||||
}
|
||||
}
|
||||
} // the app should never break due to a problem with thumbnails
|
||||
|
@ -482,7 +486,7 @@ public class ThumbnailsCacheManager {
|
|||
}
|
||||
}
|
||||
|
||||
private Bitmap doFileInBackground(File file) {
|
||||
private Bitmap doFileInBackground(File file, Type type) {
|
||||
final String imageKey;
|
||||
|
||||
if (mImageKey != null) {
|
||||
|
@ -497,14 +501,45 @@ public class ThumbnailsCacheManager {
|
|||
// Not found in disk cache
|
||||
if (thumbnail == null) {
|
||||
|
||||
int px = getThumbnailDimension();
|
||||
if (Type.IMAGE.equals(type)) {
|
||||
int px = getThumbnailDimension();
|
||||
|
||||
Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px);
|
||||
Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px);
|
||||
|
||||
if (bitmap != null) {
|
||||
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
|
||||
if (bitmap != null) {
|
||||
thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px);
|
||||
}
|
||||
} else if (Type.VIDEO.equals(type)) {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
try {
|
||||
retriever.setDataSource(file.getAbsolutePath());
|
||||
thumbnail = retriever.getFrameAtTime(-1);
|
||||
} catch (Exception ex) {
|
||||
// can't create a bitmap
|
||||
Log_OC.w(TAG, "Failed to create bitmap from video " + file.getAbsolutePath());
|
||||
} finally {
|
||||
try {
|
||||
retriever.release();
|
||||
} catch (RuntimeException ex) {
|
||||
// Ignore failure at this point.
|
||||
Log_OC.w(TAG, "Failed release MediaMetadataRetriever for " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (thumbnail != null) {
|
||||
// Scale down bitmap if too large.
|
||||
int px = getThumbnailDimension();
|
||||
int width = thumbnail.getWidth();
|
||||
int height = thumbnail.getHeight();
|
||||
int max = Math.max(width, height);
|
||||
if (max > px) {
|
||||
thumbnail = BitmapUtils.scaleBitmap(thumbnail, px, width, height, max);
|
||||
thumbnail = addThumbnailToCache(imageKey, thumbnail, file.getPath(), px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/**
|
||||
* ownCloud Android client application
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author LukeOwncloud
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
* Copyright (C) 2016 ownCloud Inc.
|
||||
* @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 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.
|
||||
* 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/>.
|
||||
* 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;
|
||||
|
||||
|
@ -26,12 +26,8 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import com.evernote.android.job.JobManager;
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
|
||||
import com.owncloud.android.db.UploadResult;
|
||||
|
@ -39,14 +35,9 @@ 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 com.owncloud.android.services.AutoUploadJob;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Observable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Database helper for storing list of files to be uploaded, including status
|
||||
|
@ -130,6 +121,8 @@ public class UploadsStorageManager extends Observable {
|
|||
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());
|
||||
cv.put(ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY, ocUpload.isWhileChargingOnly() ? 1 : 0);
|
||||
cv.put(ProviderTableMeta.UPLOADS_IS_WIFI_ONLY, ocUpload.isUseWifiOnly() ? 1 : 0);
|
||||
|
||||
Uri result = getDB().insert(ProviderTableMeta.CONTENT_URI_UPLOADS, cv);
|
||||
|
||||
|
@ -163,9 +156,9 @@ public class UploadsStorageManager extends Observable {
|
|||
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())}
|
||||
cv,
|
||||
ProviderTableMeta._ID + "=?",
|
||||
new String[]{String.valueOf(ocUpload.getUploadId())}
|
||||
);
|
||||
|
||||
Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath());
|
||||
|
@ -188,15 +181,15 @@ public class UploadsStorageManager extends Observable {
|
|||
|
||||
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() + ")");
|
||||
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) {
|
||||
if (localPath != null) {
|
||||
upload.setLocalPath(localPath);
|
||||
}
|
||||
if (status == UploadStatus.UPLOAD_SUCCEEDED) {
|
||||
|
@ -221,7 +214,7 @@ public class UploadsStorageManager extends Observable {
|
|||
* @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,
|
||||
private int updateUploadStatus(long id, UploadStatus status, UploadResult result, String remotePath,
|
||||
String localPath) {
|
||||
//Log_OC.v(TAG, "Updating "+filepath+" with uploadStatus="+status +" and result="+result);
|
||||
|
||||
|
@ -236,7 +229,7 @@ public class UploadsStorageManager extends Observable {
|
|||
|
||||
if (c.getCount() != 1) {
|
||||
Log_OC.e(TAG, c.getCount() + " items for id=" + id
|
||||
+ " available in UploadDb. Expected 1. Failed to update upload db.");
|
||||
+ " available in UploadDb. Expected 1. Failed to update upload db.");
|
||||
} else {
|
||||
returnValue = updateUploadInternal(c, status, result, remotePath, localPath);
|
||||
}
|
||||
|
@ -266,7 +259,7 @@ public class UploadsStorageManager extends Observable {
|
|||
public int removeUpload(OCUpload upload) {
|
||||
int result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta._ID + "=?" ,
|
||||
ProviderTableMeta._ID + "=?",
|
||||
new String[]{Long.toString(upload.getUploadId())}
|
||||
);
|
||||
Log_OC.d(TAG, "delete returns " + result + " for upload " + upload);
|
||||
|
@ -287,7 +280,7 @@ public class UploadsStorageManager extends Observable {
|
|||
public int removeUpload(String accountName, String remotePath) {
|
||||
int result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?" ,
|
||||
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);
|
||||
|
@ -374,68 +367,21 @@ public class UploadsStorageManager extends Observable {
|
|||
return upload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all uploads which are currently being uploaded or waiting in the queue to be uploaded.
|
||||
*/
|
||||
public OCUpload[] getCurrentAndPendingUploads() {
|
||||
public OCUpload[] getCurrentAndPendingUploadsForCurrentAccount() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
OCUpload[] uploads = getUploads(
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + " OR " +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + " OR " +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
null
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value +
|
||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.LOCK_FAILED.getValue() +
|
||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"==" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
" AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
|
||||
// add pending Jobs
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return uploads;
|
||||
} else {
|
||||
List<OCUpload> result = getPendingJobs();
|
||||
Collections.addAll(result, uploads);
|
||||
return result.toArray(uploads);
|
||||
}
|
||||
}
|
||||
return uploads;
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private List<OCUpload> getPendingJobs() {
|
||||
Set<JobRequest> jobRequests = JobManager.create(mContext).getAllJobRequestsForTag(AutoUploadJob.TAG);
|
||||
|
||||
ArrayList<OCUpload> list = new ArrayList<>();
|
||||
|
||||
for (JobRequest ji : jobRequests) {
|
||||
PersistableBundleCompat extras = ji.getExtras();
|
||||
OCUpload upload = new OCUpload(extras.getString("filePath", ""),
|
||||
extras.getString("remotePath", ""),
|
||||
extras.getString("account", ""));
|
||||
|
||||
list.add(upload);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public void cancelPendingAutoUploadJobsForAccount(Account account) {
|
||||
JobManager jobManager = JobManager.create(mContext);
|
||||
for (JobRequest ji: jobManager.getAllJobRequestsForTag(AutoUploadJob.TAG)) {
|
||||
if (ji.getExtras().getString(AutoUploadJob.ACCOUNT, "").equalsIgnoreCase(account.name)) {
|
||||
jobManager.cancel(ji.getJobId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public void cancelPendingJob(String accountName, String remotePath){
|
||||
JobManager jobManager = JobManager.create(mContext);
|
||||
Set<JobRequest> jobRequests = jobManager.getAllJobRequests();
|
||||
|
||||
for (JobRequest ji : jobRequests) {
|
||||
PersistableBundleCompat extras = ji.getExtras();
|
||||
if (remotePath.equalsIgnoreCase(extras.getString("remotePath", "")) &&
|
||||
accountName.equalsIgnoreCase(extras.getString("account", ""))) {
|
||||
jobManager.cancel(ji.getJobId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -447,6 +393,13 @@ public class UploadsStorageManager extends Observable {
|
|||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null);
|
||||
}
|
||||
|
||||
public OCUpload[] getFinishedUploadsForCurrentAccount() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{account.name});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all uploads which where successfully completed.
|
||||
*/
|
||||
|
@ -455,16 +408,33 @@ public class UploadsStorageManager extends Observable {
|
|||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, null);
|
||||
}
|
||||
|
||||
public OCUpload[] getFailedButNotDelayedUploadsForCurrentAccount() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all failed uploads, except for those that were not performed due to lack of Wifi connection
|
||||
* @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection.
|
||||
* Get all failed uploads, except for those that were not performed due to lack of Wifi connection.
|
||||
*
|
||||
* @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection.
|
||||
*/
|
||||
public OCUpload[] getFailedButNotDelayedUploads() {
|
||||
|
||||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
null
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -473,13 +443,22 @@ public class UploadsStorageManager extends Observable {
|
|||
}
|
||||
|
||||
public long clearFailedButNotDelayedUploads() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
long result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
null
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
|
||||
Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi");
|
||||
if (result > 0) {
|
||||
notifyObserversNow();
|
||||
|
@ -488,11 +467,14 @@ public class UploadsStorageManager extends Observable {
|
|||
}
|
||||
|
||||
public long clearSuccessfulUploads() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
long result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "=="+ UploadStatus.UPLOAD_SUCCEEDED.value, null
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{account.name}
|
||||
);
|
||||
|
||||
Log_OC.d(TAG, "delete all successful uploads");
|
||||
if (result > 0) {
|
||||
notifyObserversNow();
|
||||
|
@ -501,21 +483,31 @@ public class UploadsStorageManager extends Observable {
|
|||
}
|
||||
|
||||
public long clearAllFinishedButNotDelayedUploads() {
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(mContext);
|
||||
|
||||
String[] whereArgs = new String[2];
|
||||
String[] whereArgs = new String[3];
|
||||
whereArgs[0] = String.valueOf(UploadStatus.UPLOAD_SUCCEEDED.value);
|
||||
whereArgs[1] = String.valueOf(UploadStatus.UPLOAD_FAILED.value);
|
||||
whereArgs[2] = account.name;
|
||||
long result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=? AND " +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=?" +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
whereArgs
|
||||
);
|
||||
|
||||
Log_OC.d(TAG, "delete all finished uploads");
|
||||
if (result > 0) {
|
||||
notifyObserversNow();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -528,28 +520,28 @@ public class UploadsStorageManager extends Observable {
|
|||
|
||||
if (uploadResult.isCancelled()) {
|
||||
removeUpload(
|
||||
upload.getAccount().name,
|
||||
upload.getRemotePath()
|
||||
upload.getAccount().name,
|
||||
upload.getRemotePath()
|
||||
);
|
||||
} else {
|
||||
String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
|
||||
? upload.getStoragePath() : null;
|
||||
? upload.getStoragePath() : null;
|
||||
|
||||
if (uploadResult.isSuccess()) {
|
||||
updateUploadStatus(
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_SUCCEEDED,
|
||||
UploadResult.UPLOADED,
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_SUCCEEDED,
|
||||
UploadResult.UPLOADED,
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
);
|
||||
} else {
|
||||
updateUploadStatus(
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_FAILED,
|
||||
UploadResult.fromOperationResult(uploadResult),
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_FAILED,
|
||||
UploadResult.fromOperationResult(uploadResult),
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -560,14 +552,14 @@ public class UploadsStorageManager extends Observable {
|
|||
*/
|
||||
public void updateDatabaseUploadStart(UploadFileOperation upload) {
|
||||
String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour())
|
||||
? upload.getStoragePath() : null;
|
||||
? upload.getStoragePath() : null;
|
||||
|
||||
updateUploadStatus(
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_IN_PROGRESS,
|
||||
UploadResult.UNKNOWN,
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
upload.getOCUploadId(),
|
||||
UploadStatus.UPLOAD_IN_PROGRESS,
|
||||
UploadResult.UNKNOWN,
|
||||
upload.getRemotePath(),
|
||||
localPath
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -576,7 +568,7 @@ public class UploadsStorageManager extends Observable {
|
|||
* 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.
|
||||
* @return Number of uploads which status was changed.
|
||||
*/
|
||||
public int failInProgressUploads(UploadResult fail) {
|
||||
Log_OC.v(TAG, "Updating state of any killed upload");
|
||||
|
@ -584,16 +576,16 @@ public class UploadsStorageManager extends Observable {
|
|||
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()
|
||||
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())}
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
cv,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "=?",
|
||||
new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())}
|
||||
);
|
||||
|
||||
if (result == 0) {
|
||||
|
@ -602,15 +594,7 @@ public class UploadsStorageManager extends Observable {
|
|||
Log_OC.w(TAG, Integer.toString(result) + " uploads where abruptly interrupted");
|
||||
notifyObserversNow();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int removeAccountUploads(Account account) {
|
||||
Log_OC.v(TAG, "Delete all uploads for account " + account.name);
|
||||
return getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=?",
|
||||
new String[]{account.name});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ 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 {
|
||||
|
||||
|
@ -49,7 +48,7 @@ public class OCUpload implements Parcelable {
|
|||
private long mId;
|
||||
|
||||
/**
|
||||
* Absolute path in the local file system to the file to be uploaded
|
||||
* Absolute path in the local file system to the file to be uploaded.
|
||||
*/
|
||||
private String mLocalPath;
|
||||
|
||||
|
@ -64,7 +63,7 @@ public class OCUpload implements Parcelable {
|
|||
private String mAccountName;
|
||||
|
||||
/**
|
||||
* File size
|
||||
* File size.
|
||||
*/
|
||||
private long mFileSize;
|
||||
|
||||
|
@ -77,14 +76,17 @@ public class OCUpload implements Parcelable {
|
|||
* 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.
|
||||
*/
|
||||
|
@ -95,14 +97,23 @@ public class OCUpload implements Parcelable {
|
|||
*/
|
||||
private int mCreatedBy;
|
||||
|
||||
/*
|
||||
/**
|
||||
* When the upload ended
|
||||
*/
|
||||
private long mUploadEndTimeStamp;
|
||||
|
||||
/**
|
||||
* Upload only via wifi?
|
||||
*/
|
||||
private boolean mIsUseWifiOnly;
|
||||
|
||||
/**
|
||||
* Main constructor
|
||||
* Upload only if phone being charged?
|
||||
*/
|
||||
private boolean mIsWhileChargingOnly;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -124,9 +135,8 @@ public class OCUpload implements Parcelable {
|
|||
mAccountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience constructor to reupload already existing {@link OCFile}s
|
||||
* 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.
|
||||
|
@ -135,7 +145,6 @@ public class OCUpload implements Parcelable {
|
|||
this(ocFile.getStoragePath(), ocFile.getRemotePath(), account.name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset all the fields to default values.
|
||||
*/
|
||||
|
@ -151,6 +160,8 @@ public class OCUpload implements Parcelable {
|
|||
mUploadStatus = UploadStatus.UPLOAD_IN_PROGRESS;
|
||||
mLastResult = UploadResult.UNKNOWN;
|
||||
mCreatedBy = UploadFileOperation.CREATED_BY_USER;
|
||||
mIsUseWifiOnly = true;
|
||||
mIsWhileChargingOnly = false;
|
||||
}
|
||||
|
||||
// Getters & Setters
|
||||
|
@ -230,7 +241,6 @@ public class OCUpload implements Parcelable {
|
|||
mFileSize = fileSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the mimeType
|
||||
*/
|
||||
|
@ -340,6 +350,28 @@ public class OCUpload implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the isUseWifiOnly
|
||||
*/
|
||||
public boolean isUseWifiOnly() {
|
||||
return mIsUseWifiOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isUseWifiOnly the isUseWifiOnly to set
|
||||
*/
|
||||
public void setUseWifiOnly(boolean isUseWifiOnly) {
|
||||
this.mIsUseWifiOnly = isUseWifiOnly;
|
||||
}
|
||||
|
||||
public void setWhileChargingOnly(boolean isWhileChargingOnly) {
|
||||
this.mIsWhileChargingOnly = isWhileChargingOnly;
|
||||
}
|
||||
|
||||
public boolean isWhileChargingOnly() {
|
||||
return mIsWhileChargingOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct from parcel
|
||||
*
|
||||
|
@ -369,9 +401,10 @@ public class OCUpload implements Parcelable {
|
|||
mLastResult = UploadResult.UNKNOWN;
|
||||
}
|
||||
mCreatedBy = source.readInt();
|
||||
mIsUseWifiOnly = (source.readInt() == 1);
|
||||
mIsWhileChargingOnly = (source.readInt() == 1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return this.hashCode();
|
||||
|
@ -390,6 +423,8 @@ public class OCUpload implements Parcelable {
|
|||
dest.writeLong(mUploadEndTimeStamp);
|
||||
dest.writeString(((mLastResult == null) ? "" : mLastResult.name()));
|
||||
dest.writeInt(mCreatedBy);
|
||||
dest.writeInt(mIsUseWifiOnly ? 1 : 0);
|
||||
dest.writeInt(mIsWhileChargingOnly ? 1 : 0);
|
||||
}
|
||||
|
||||
enum CanUploadFileNowStatus {NOW, LATER, FILE_GONE, ERROR}
|
||||
|
|
|
@ -48,6 +48,8 @@ public abstract class PreferenceManager {
|
|||
private static final String PREF__LEGACY_CLEAN = "legacyClean";
|
||||
private static final String PREF__AUTO_UPLOAD_UPDATE_PATH = "autoUploadPathUpdate";
|
||||
private static final String PREF__PUSH_TOKEN = "pushToken";
|
||||
private static final String PREF__AUTO_UPLOAD_SPLIT_OUT = "autoUploadEntriesSplitOut";
|
||||
private static final String PREF__AUTO_UPLOAD_INIT = "autoUploadInit";
|
||||
|
||||
public static void setPushToken(Context context, String pushToken) {
|
||||
saveStringPreferenceNow(context, PREF__PUSH_TOKEN, pushToken);
|
||||
|
@ -198,6 +200,10 @@ public abstract class PreferenceManager {
|
|||
saveBooleanPreference(context, AUTO_PREF__SORT_ASCENDING, ascending);
|
||||
}
|
||||
|
||||
public static boolean getAutoUploadInit(Context context) {
|
||||
return getDefaultSharedPreferences(context).getBoolean(PREF__AUTO_UPLOAD_INIT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the legacy cleaning flag last set.
|
||||
*
|
||||
|
@ -218,6 +224,15 @@ public abstract class PreferenceManager {
|
|||
return getDefaultSharedPreferences(context).getBoolean(PREF__AUTO_UPLOAD_UPDATE_PATH, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the auto upload split out flag last set.
|
||||
*
|
||||
* @param context Caller {@link Context}, used to access to shared preferences manager.
|
||||
* @return ascending order the legacy cleaning flag, default is false
|
||||
*/
|
||||
public static boolean getAutoUploadSplitEntries(Context context) {
|
||||
return getDefaultSharedPreferences(context).getBoolean(PREF__AUTO_UPLOAD_SPLIT_OUT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the legacy cleaning flag which the user has set last.
|
||||
|
@ -229,6 +244,10 @@ public abstract class PreferenceManager {
|
|||
saveBooleanPreference(context, PREF__LEGACY_CLEAN, legacyClean);
|
||||
}
|
||||
|
||||
public static void setAutoUploadInit(Context context, boolean autoUploadInit) {
|
||||
saveBooleanPreference(context, PREF__AUTO_UPLOAD_INIT, autoUploadInit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the legacy cleaning flag which the user has set last.
|
||||
*
|
||||
|
@ -239,6 +258,15 @@ public abstract class PreferenceManager {
|
|||
saveBooleanPreference(context, PREF__AUTO_UPLOAD_UPDATE_PATH, pathUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the flag for split entries magic
|
||||
*
|
||||
* @param context Caller {@link Context}, used to access to shared preferences manager.
|
||||
* @param splitOut flag if it is a auto upload path update
|
||||
*/
|
||||
public static void setAutoUploadSplitEntries(Context context, boolean splitOut) {
|
||||
saveBooleanPreference(context, PREF__AUTO_UPLOAD_SPLIT_OUT, splitOut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the uploader behavior which the user has set last.
|
||||
|
@ -280,7 +308,7 @@ public abstract class PreferenceManager {
|
|||
saveFloatPreference(context, AUTO_PREF__GRID_COLUMNS, gridColumns);
|
||||
}
|
||||
|
||||
public static void saveBooleanPreference(Context context, String key, boolean value) {
|
||||
private static void saveBooleanPreference(Context context, String key, boolean value) {
|
||||
SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
|
||||
appPreferences.putBoolean(key, value).apply();
|
||||
}
|
||||
|
@ -301,17 +329,11 @@ public abstract class PreferenceManager {
|
|||
appPreferences.putInt(key, value).apply();
|
||||
}
|
||||
|
||||
public static void saveFloatPreference(Context context, String key, float value) {
|
||||
private static void saveFloatPreference(Context context, String key, float value) {
|
||||
SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
|
||||
appPreferences.putFloat(key, value).apply();
|
||||
}
|
||||
|
||||
private static void saveLongPreference(Context context, String key, long value) {
|
||||
SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
|
||||
appPreferences.putLong(key, value);
|
||||
appPreferences.apply();
|
||||
}
|
||||
|
||||
public static SharedPreferences getDefaultSharedPreferences(Context context) {
|
||||
return android.preference.PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import com.owncloud.android.MainApp;
|
|||
public class ProviderMeta {
|
||||
|
||||
public static final String DB_NAME = "filelist";
|
||||
public static final int DB_VERSION = 22;
|
||||
public static final int DB_VERSION = 23;
|
||||
|
||||
private ProviderMeta() {
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ public class ProviderMeta {
|
|||
public static final String EXTERNAL_LINKS_TABLE_NAME = "external_links";
|
||||
public static final String ARBITRARY_DATA_TABLE_NAME = "arbitrary_data";
|
||||
public static final String VIRTUAL_TABLE_NAME = "virtual";
|
||||
public static final String FILESYSTEM_TABLE_NAME = "filesystem";
|
||||
|
||||
private static final String CONTENT_PREFIX = "content://";
|
||||
|
||||
|
@ -68,6 +69,9 @@ public class ProviderMeta {
|
|||
public static final Uri CONTENT_URI_ARBITRARY_DATA = Uri.parse(CONTENT_PREFIX
|
||||
+ MainApp.getAuthority() + "/arbitrary_data");
|
||||
public static final Uri CONTENT_URI_VIRTUAL = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/virtual");
|
||||
public static final Uri CONTENT_URI_FILESYSTEM = Uri.parse(CONTENT_PREFIX
|
||||
+ MainApp.getAuthority() + "/filesystem");
|
||||
|
||||
|
||||
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";
|
||||
|
@ -169,6 +173,8 @@ public class ProviderMeta {
|
|||
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";
|
||||
public static final String UPLOADS_IS_WHILE_CHARGING_ONLY = "is_while_charging_only";
|
||||
public static final String UPLOADS_IS_WIFI_ONLY = "is_wifi_only";
|
||||
|
||||
// Columns of synced folder table
|
||||
public static final String SYNCED_FOLDER_LOCAL_PATH = "local_path";
|
||||
|
@ -176,6 +182,7 @@ public class ProviderMeta {
|
|||
public static final String SYNCED_FOLDER_WIFI_ONLY = "wifi_only";
|
||||
public static final String SYNCED_FOLDER_CHARGING_ONLY = "charging_only";
|
||||
public static final String SYNCED_FOLDER_ENABLED = "enabled";
|
||||
public static final String SYNCED_FOLDER_TYPE = "type";
|
||||
public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date";
|
||||
public static final String SYNCED_FOLDER_ACCOUNT = "account";
|
||||
public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option";
|
||||
|
@ -192,8 +199,17 @@ public class ProviderMeta {
|
|||
public static final String ARBITRARY_DATA_KEY = "key";
|
||||
public static final String ARBITRARY_DATA_VALUE = "value";
|
||||
|
||||
|
||||
// Columns of virtual
|
||||
public static final String VIRTUAL_TYPE = "type";
|
||||
public static final String VIRTUAL_OCFILE_ID = "ocfile_id";
|
||||
|
||||
// Columns of filesystem data table
|
||||
public static final String FILESYSTEM_FILE_LOCAL_PATH = "local_path";
|
||||
public static final String FILESYSTEM_FILE_MODIFIED = "modified_at";
|
||||
public static final String FILESYSTEM_FILE_IS_FOLDER = "is_folder";
|
||||
public static final String FILESYSTEM_FILE_FOUND_RECENTLY = "found_at";
|
||||
public static final String FILESYSTEM_FILE_SENT_FOR_UPLOAD = "upload_triggered";
|
||||
public static final String FILESYSTEM_SYNCED_FOLDER_ID = "syncedfolder_id";
|
||||
}
|
||||
}
|
|
@ -35,7 +35,8 @@ public enum UploadResult {
|
|||
DELAYED_FOR_WIFI(9),
|
||||
SERVICE_INTERRUPTED(10),
|
||||
DELAYED_FOR_CHARGING(11),
|
||||
MAINTENANCE_MODE(12);
|
||||
MAINTENANCE_MODE(12),
|
||||
LOCK_FAILED(13);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
@ -77,6 +78,8 @@ public enum UploadResult {
|
|||
return DELAYED_FOR_CHARGING;
|
||||
case 12:
|
||||
return MAINTENANCE_MODE;
|
||||
case 13:
|
||||
return LOCK_FAILED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -120,6 +123,8 @@ public enum UploadResult {
|
|||
return UNKNOWN;
|
||||
case MAINTENANCE_MODE:
|
||||
return MAINTENANCE_MODE;
|
||||
case LOCK_FAILED:
|
||||
return LOCK_FAILED;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
/**
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.files;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore.Images;
|
||||
import android.provider.MediaStore.Video;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.db.PreferenceManager;
|
||||
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 {
|
||||
|
||||
private static final String TAG = InstantUploadBroadcastReceiver.class.getName();
|
||||
// Image action
|
||||
// Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
|
||||
private static final 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
|
||||
private static final 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
|
||||
private static final String NEW_VIDEO_ACTION = "android.hardware.action.NEW_VIDEO";
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
Log_OC.d(TAG, "Received: " + intent.getAction());
|
||||
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);
|
||||
Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
|
||||
} else if (intent.getAction().equals(NEW_VIDEO_ACTION)) {
|
||||
handleNewVideoAction(context, intent);
|
||||
Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_VIDEO");
|
||||
} else {
|
||||
Log_OC.e(TAG, "Incorrect intent received: " + intent.getAction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNewPictureAction(Context context, Intent intent) {
|
||||
Cursor c = null;
|
||||
String file_path = null;
|
||||
String file_name = null;
|
||||
String mime_type = null;
|
||||
long date_taken = 0;
|
||||
|
||||
Log_OC.i(TAG, "New photo received");
|
||||
|
||||
if (!PreferenceManager.instantPictureUploadEnabled(context)) {
|
||||
Log_OC.d(TAG, "Instant picture upload disabled, ignoring new picture");
|
||||
return;
|
||||
}
|
||||
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(context);
|
||||
if (account == null) {
|
||||
Log_OC.w(TAG, "No account found for instant upload, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
String[] CONTENT_PROJECTION = {
|
||||
Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
|
||||
|
||||
// if < Jelly Bean permission must be accepted during installation
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
int permissionCheck = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
|
||||
if (android.content.pm.PackageManager.PERMISSION_GRANTED != permissionCheck) {
|
||||
Log_OC.w(TAG, "Read external storage permission isn't granted, aborting");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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(Images.Media.DATA));
|
||||
file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
|
||||
mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE));
|
||||
date_taken = System.currentTimeMillis();
|
||||
c.close();
|
||||
|
||||
if (file_path.equals(lastUploadedPhotoPath)) {
|
||||
Log_OC.d(TAG, "Duplicate detected: " + file_path + ". Ignore.");
|
||||
return;
|
||||
}
|
||||
|
||||
lastUploadedPhotoPath = file_path;
|
||||
Log_OC.d(TAG, "Path: " + file_path + "");
|
||||
|
||||
new FileUploader.UploadRequester();
|
||||
|
||||
int behaviour = getUploadBehaviour(context);
|
||||
Boolean subfolderByDate = PreferenceManager.instantPictureUploadPathUseSubfolders(context);
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String uploadPathdef = context.getString(R.string.instant_upload_path);
|
||||
String uploadPath = pref.getString("instant_upload_path", uploadPathdef);
|
||||
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
context,
|
||||
account,
|
||||
file_path,
|
||||
FileStorageUtils.getInstantUploadFilePath(uploadPath, file_name, date_taken, subfolderByDate),
|
||||
behaviour,
|
||||
mime_type,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE
|
||||
);
|
||||
}
|
||||
|
||||
private Integer getUploadBehaviour(Context context) {
|
||||
SharedPreferences appPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String behaviour = appPreferences.getString("prefs_instant_behaviour", "NOTHING");
|
||||
|
||||
if (behaviour.equalsIgnoreCase("NOTHING")) {
|
||||
Log_OC.d(TAG, "upload file and do nothing");
|
||||
return FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
} else if (behaviour.equalsIgnoreCase("MOVE")) {
|
||||
Log_OC.d(TAG, "upload file and move file to oc folder");
|
||||
return FileUploader.LOCAL_BEHAVIOUR_MOVE;
|
||||
} else if (behaviour.equalsIgnoreCase("DELETE")) {
|
||||
Log_OC.d(TAG, "upload file and delete original file");
|
||||
return FileUploader.LOCAL_BEHAVIOUR_DELETE;
|
||||
}
|
||||
return FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
}
|
||||
|
||||
private void handleNewVideoAction(Context context, Intent intent) {
|
||||
Cursor c = null;
|
||||
String file_path = null;
|
||||
String file_name = null;
|
||||
String mime_type = null;
|
||||
long date_taken = 0;
|
||||
|
||||
Log_OC.i(TAG, "New video received");
|
||||
|
||||
if (!PreferenceManager.instantVideoUploadEnabled(context)) {
|
||||
Log_OC.d(TAG, "Instant video upload disabled, ignoring new video");
|
||||
return;
|
||||
}
|
||||
|
||||
Account account = AccountUtils.getCurrentOwnCloudAccount(context);
|
||||
if (account == null) {
|
||||
Log_OC.w(TAG, "No account found for instant upload, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
date_taken = System.currentTimeMillis();
|
||||
Log_OC.d(TAG, file_path + "");
|
||||
|
||||
int behaviour = getUploadBehaviour(context);
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
context,
|
||||
account,
|
||||
file_path,
|
||||
FileStorageUtils.getInstantVideoUploadFilePath(context, file_name, date_taken),
|
||||
behaviour,
|
||||
mime_type,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_VIDEO
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,223 +0,0 @@
|
|||
/**
|
||||
* 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.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.owncloud.android.db.PreferenceManager;
|
||||
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");
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED) &&
|
||||
(PreferenceManager.instantPictureUploadEnabled(context) &&
|
||||
PreferenceManager.instantPictureUploadWhenChargingOnly(context)) ||
|
||||
(PreferenceManager.instantVideoUploadEnabled(context) &&
|
||||
PreferenceManager.instantVideoUploadWhenChargingOnly(context))
|
||||
) {
|
||||
// for the moment, only recovery of instant uploads, similar to behaviour in release 1.9.1
|
||||
Log_OC.d(TAG, "Requesting retry of instant uploads (& friends) due to charging");
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.retryFailedUploads(
|
||||
context,
|
||||
null,
|
||||
UploadResult.DELAYED_FOR_CHARGING // for the rest of enqueued when Wifi fell
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.isAppConnectedViaUnmeteredWiFi(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 (
|
||||
(PreferenceManager.instantPictureUploadEnabled(context) &&
|
||||
PreferenceManager.instantPictureUploadViaWiFiOnly(context)) ||
|
||||
(PreferenceManager.instantVideoUploadEnabled(context) &&
|
||||
PreferenceManager.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() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
|
@ -34,7 +34,6 @@ import android.app.Service;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
|
@ -45,6 +44,9 @@ import android.os.Process;
|
|||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.Device;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||
|
@ -77,6 +79,8 @@ import java.util.Iterator;
|
|||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Service for uploading files. Invoke using context.startService(...).
|
||||
*
|
||||
|
@ -144,6 +148,9 @@ public class FileUploader extends Service
|
|||
* Key to signal what is the origin of the upload request
|
||||
*/
|
||||
public static final String KEY_CREATED_BY = "CREATED_BY";
|
||||
|
||||
public static final String KEY_WHILE_ON_WIFI_ONLY = "KEY_ON_WIFI_ONLY";
|
||||
|
||||
/**
|
||||
* Set to true if upload is to performed only when phone is being charged.
|
||||
*/
|
||||
|
@ -194,7 +201,6 @@ public class FileUploader extends Service
|
|||
sendBroadcastUploadStarted(mCurrentUpload);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class providing methods to ease requesting commands to {@link FileUploader} .
|
||||
*
|
||||
|
@ -214,7 +220,9 @@ public class FileUploader extends Service
|
|||
String[] mimeTypes,
|
||||
Integer behaviour,
|
||||
Boolean createRemoteFolder,
|
||||
int createdBy
|
||||
int createdBy,
|
||||
boolean requiresWifi,
|
||||
boolean requiresCharging
|
||||
) {
|
||||
Intent intent = new Intent(context, FileUploader.class);
|
||||
|
||||
|
@ -225,15 +233,69 @@ public class FileUploader extends Service
|
|||
intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour);
|
||||
intent.putExtra(FileUploader.KEY_CREATE_REMOTE_FOLDER, createRemoteFolder);
|
||||
intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy);
|
||||
intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi);
|
||||
intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging);
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
public void uploadFileWithOverwrite(
|
||||
Context context,
|
||||
Account account,
|
||||
String[] localPaths,
|
||||
String[] remotePaths,
|
||||
String[] mimeTypes,
|
||||
Integer behaviour,
|
||||
Boolean createRemoteFolder,
|
||||
int createdBy,
|
||||
boolean requiresWifi,
|
||||
boolean requiresCharging,
|
||||
boolean overwrite
|
||||
) {
|
||||
Intent intent = new Intent(context, FileUploader.class);
|
||||
|
||||
intent.putExtra(FileUploader.KEY_ACCOUNT, account);
|
||||
intent.putExtra(FileUploader.KEY_LOCAL_FILE, localPaths);
|
||||
intent.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
|
||||
intent.putExtra(FileUploader.KEY_MIME_TYPE, mimeTypes);
|
||||
intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour);
|
||||
intent.putExtra(FileUploader.KEY_CREATE_REMOTE_FOLDER, createRemoteFolder);
|
||||
intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy);
|
||||
intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi);
|
||||
intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging);
|
||||
intent.putExtra(FileUploader.KEY_FORCE_OVERWRITE, overwrite);
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to upload a file
|
||||
*/
|
||||
public void uploadFileWithOverwrite(Context context, Account account, String localPath, String remotePath, int
|
||||
behaviour, String mimeType, boolean createRemoteFile, int createdBy, boolean requiresWifi,
|
||||
boolean requiresCharging, boolean overwrite) {
|
||||
|
||||
uploadFileWithOverwrite(
|
||||
context,
|
||||
account,
|
||||
new String[]{localPath},
|
||||
new String[]{remotePath},
|
||||
new String[]{mimeType},
|
||||
behaviour,
|
||||
createRemoteFile,
|
||||
createdBy,
|
||||
requiresWifi,
|
||||
requiresCharging,
|
||||
overwrite
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to upload a new single file
|
||||
*/
|
||||
public void uploadNewFile(Context context, Account account, String localPath, String remotePath, int
|
||||
behaviour, String mimeType, boolean createRemoteFile, int createdBy) {
|
||||
behaviour, String mimeType, boolean createRemoteFile, int createdBy, boolean requiresWifi,
|
||||
boolean requiresCharging) {
|
||||
|
||||
uploadNewFile(
|
||||
context,
|
||||
|
@ -243,7 +305,9 @@ public class FileUploader extends Service
|
|||
new String[]{mimeType},
|
||||
behaviour,
|
||||
createRemoteFile,
|
||||
createdBy
|
||||
createdBy,
|
||||
requiresWifi,
|
||||
requiresCharging
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -288,7 +352,6 @@ public class FileUploader extends Service
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retry a subset of all the stored failed uploads.
|
||||
*
|
||||
|
@ -306,7 +369,7 @@ public class FileUploader extends Service
|
|||
boolean accountMatch;
|
||||
for ( OCUpload failedUpload: failedUploads) {
|
||||
accountMatch = (account == null || account.name.equals(failedUpload.getAccountName()));
|
||||
resultMatch = (uploadResult == null || uploadResult.equals(failedUpload.getLastResult()));
|
||||
resultMatch = ((uploadResult == null || uploadResult.equals(failedUpload.getLastResult())));
|
||||
if (accountMatch && resultMatch) {
|
||||
if (currentAccount == null ||
|
||||
!currentAccount.name.equals(failedUpload.getAccountName())) {
|
||||
|
@ -330,13 +393,12 @@ public class FileUploader extends Service
|
|||
i.putExtra(FileUploader.KEY_RETRY, true);
|
||||
i.putExtra(FileUploader.KEY_ACCOUNT, account);
|
||||
i.putExtra(FileUploader.KEY_RETRY_UPLOAD, upload);
|
||||
|
||||
context.startService(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Service initialization
|
||||
*/
|
||||
|
@ -370,7 +432,6 @@ public class FileUploader extends Service
|
|||
am.addOnAccountsUpdatedListener(this, null, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Service clean-up when restarted after being killed
|
||||
*/
|
||||
|
@ -379,7 +440,6 @@ public class FileUploader extends Service
|
|||
mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Service clean up
|
||||
*/
|
||||
|
@ -399,7 +459,6 @@ public class FileUploader extends Service
|
|||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Entry point to add one or several files to the queue of uploads.
|
||||
*
|
||||
|
@ -426,9 +485,14 @@ public class FileUploader extends Service
|
|||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
OwnCloudVersion ocv = AccountUtils.getServerVersion(account);
|
||||
|
||||
boolean chunked = ocv.isChunkedUploadSupported();
|
||||
|
||||
boolean onWifiOnly = intent.getBooleanExtra(KEY_WHILE_ON_WIFI_ONLY, false);
|
||||
boolean whileChargingOnly = intent.getBooleanExtra(KEY_WHILE_CHARGING_ONLY, false);
|
||||
|
||||
if (!retry) {
|
||||
|
||||
if (!(intent.hasExtra(KEY_LOCAL_FILE) ||
|
||||
intent.hasExtra(KEY_FILE))) {
|
||||
Log_OC.e(TAG, "Not enough information provided in intent");
|
||||
|
@ -451,7 +515,6 @@ public class FileUploader extends Service
|
|||
mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
|
||||
}
|
||||
|
||||
|
||||
boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
|
||||
int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_FORGET);
|
||||
|
||||
|
@ -503,10 +566,12 @@ public class FileUploader extends Service
|
|||
ocUpload.setCreateRemoteFolder(isCreateRemoteFolder);
|
||||
ocUpload.setCreatedBy(createdBy);
|
||||
ocUpload.setLocalAction(localAction);
|
||||
/*ocUpload.setUseWifiOnly(isUseWifiOnly);
|
||||
ocUpload.setWhileChargingOnly(isWhileChargingOnly);*/
|
||||
ocUpload.setUseWifiOnly(onWifiOnly);
|
||||
ocUpload.setWhileChargingOnly(whileChargingOnly);
|
||||
|
||||
ocUpload.setUploadStatus(UploadStatus.UPLOAD_IN_PROGRESS);
|
||||
|
||||
|
||||
newUpload = new UploadFileOperation(
|
||||
account,
|
||||
files[i],
|
||||
|
@ -514,7 +579,9 @@ public class FileUploader extends Service
|
|||
chunked,
|
||||
forceOverwrite,
|
||||
localAction,
|
||||
this
|
||||
this,
|
||||
onWifiOnly,
|
||||
whileChargingOnly
|
||||
);
|
||||
newUpload.setCreatedBy(createdBy);
|
||||
if (isCreateRemoteFolder) {
|
||||
|
@ -561,6 +628,9 @@ public class FileUploader extends Service
|
|||
}
|
||||
OCUpload upload = intent.getParcelableExtra(KEY_RETRY_UPLOAD);
|
||||
|
||||
onWifiOnly = upload.isUseWifiOnly();
|
||||
whileChargingOnly = upload.isWhileChargingOnly();
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
account,
|
||||
null,
|
||||
|
@ -568,7 +638,9 @@ public class FileUploader extends Service
|
|||
chunked,
|
||||
upload.isForceOverwrite(), // TODO should be read from DB?
|
||||
upload.getLocalAction(), // TODO should be read from DB?
|
||||
this
|
||||
this,
|
||||
onWifiOnly,
|
||||
whileChargingOnly
|
||||
);
|
||||
|
||||
newUpload.addDatatransferProgressListener(this);
|
||||
|
@ -634,9 +706,8 @@ public class FileUploader extends Service
|
|||
}
|
||||
|
||||
/**
|
||||
* Binder to let client components to perform operations on the queue of
|
||||
* uploads.
|
||||
* <p/>
|
||||
* Binder to let client components to perform operations on the queue of uploads.
|
||||
*
|
||||
* It provides by itself the available operations.
|
||||
*/
|
||||
public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener {
|
||||
|
@ -645,9 +716,7 @@ public class FileUploader extends Service
|
|||
* Map of listeners that will be reported about progress of uploads from a
|
||||
* {@link FileUploaderBinder} instance
|
||||
*/
|
||||
private Map<String, OnDatatransferProgressListener> mBoundListeners =
|
||||
new HashMap<String, OnDatatransferProgressListener>();
|
||||
|
||||
private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Cancels a pending or current upload of a remote file.
|
||||
|
@ -656,7 +725,7 @@ public class FileUploader extends Service
|
|||
* @param file A file in the queue of pending uploads
|
||||
*/
|
||||
public void cancel(Account account, OCFile file) {
|
||||
cancel(account.name, file.getRemotePath());
|
||||
cancel(account.name, file.getRemotePath(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -665,7 +734,7 @@ public class FileUploader extends Service
|
|||
* @param storedUpload Upload operation persisted
|
||||
*/
|
||||
public void cancel(OCUpload storedUpload) {
|
||||
cancel(storedUpload.getAccountName(), storedUpload.getRemotePath());
|
||||
cancel(storedUpload.getAccountName(), storedUpload.getRemotePath(), null);
|
||||
|
||||
}
|
||||
|
||||
|
@ -674,8 +743,10 @@ public class FileUploader extends Service
|
|||
*
|
||||
* @param accountName Local name of an ownCloud account where the remote file will be stored.
|
||||
* @param remotePath Remote target of the upload
|
||||
*
|
||||
* Setting result code will pause rather than cancel the job
|
||||
*/
|
||||
private void cancel(String accountName, String remotePath) {
|
||||
private void cancel(String accountName, String remotePath, @Nullable ResultCode resultCode ) {
|
||||
Pair<UploadFileOperation, String> removeResult =
|
||||
mPendingUploads.remove(accountName, remotePath);
|
||||
UploadFileOperation upload = removeResult.first;
|
||||
|
@ -686,14 +757,17 @@ public class FileUploader extends Service
|
|||
|
||||
upload = mCurrentUpload;
|
||||
}
|
||||
|
||||
if (upload != null) {
|
||||
upload.cancel();
|
||||
// need to update now table in mUploadsStorageManager,
|
||||
// since the operation will not get to be run by FileUploader#uploadFile
|
||||
mUploadsStorageManager.removeUpload(accountName, remotePath);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// try to cancel job in jobScheduler
|
||||
mUploadsStorageManager.cancelPendingJob(accountName, remotePath);
|
||||
if (resultCode != null) {
|
||||
mUploadsStorageManager.updateDatabaseUploadResult(new RemoteOperationResult(resultCode), upload);
|
||||
notifyUploadResult(upload, new RemoteOperationResult(resultCode));
|
||||
} else {
|
||||
mUploadsStorageManager.removeUpload(accountName, remotePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,7 +814,6 @@ public class FileUploader extends Service
|
|||
return (mPendingUploads.contains(account.name, file.getRemotePath()));
|
||||
}
|
||||
|
||||
|
||||
public boolean isUploadingNow(OCUpload upload) {
|
||||
return (
|
||||
upload != null &&
|
||||
|
@ -751,7 +824,6 @@ public class FileUploader extends Service
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener interested in the progress of the upload for a concrete file.
|
||||
*
|
||||
|
@ -771,7 +843,6 @@ public class FileUploader extends Service
|
|||
mBoundListeners.put(targetKey, listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener interested in the progress of the upload for a concrete file.
|
||||
*
|
||||
|
@ -789,7 +860,6 @@ public class FileUploader extends Service
|
|||
mBoundListeners.put(targetKey, listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a listener interested in the progress of the upload for a concrete file.
|
||||
*
|
||||
|
@ -811,7 +881,6 @@ public class FileUploader extends Service
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a listener interested in the progress of the upload for a concrete file.
|
||||
*
|
||||
|
@ -831,7 +900,6 @@ public class FileUploader extends Service
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTransferProgress(long progressRate, long totalTransferredSoFar,
|
||||
long totalToTransfer, String fileName) {
|
||||
|
@ -840,12 +908,24 @@ public class FileUploader extends Service
|
|||
if (boundListener != null) {
|
||||
boundListener.onTransferProgress(progressRate, totalTransferredSoFar,
|
||||
totalToTransfer, fileName);
|
||||
|
||||
if (MainApp.getAppContext() != null) {
|
||||
if (mCurrentUpload.getIsWifiRequired() && !Device.getNetworkType(MainApp.getAppContext()).
|
||||
equals(JobRequest.NetworkType.UNMETERED)) {
|
||||
cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath()
|
||||
, ResultCode.DELAYED_FOR_WIFI);
|
||||
} else if (mCurrentUpload.getIsChargingRequired() &&
|
||||
!Device.isCharging(MainApp.getAppContext())) {
|
||||
cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath()
|
||||
, ResultCode.DELAYED_FOR_CHARGING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a key for the map of listeners.
|
||||
* <p/>
|
||||
*
|
||||
* TODO use method in IndexedForest, or refactor both to a common place
|
||||
* add to local database) to better policy (add to local database, then upload)
|
||||
*
|
||||
|
@ -863,7 +943,7 @@ public class FileUploader extends Service
|
|||
/**
|
||||
* Upload worker. Performs the pending uploads in the order they were
|
||||
* requested.
|
||||
* <p/>
|
||||
*
|
||||
* Created with the Looper of a new thread, started in
|
||||
* {@link FileUploader#onCreate()}.
|
||||
*/
|
||||
|
@ -958,7 +1038,7 @@ public class FileUploader extends Service
|
|||
mCurrentAccount.name,
|
||||
mCurrentUpload.getOldFile().getRemotePath()
|
||||
);
|
||||
/** TODO: grant that name is also updated for mCurrentUpload.getOCUploadId */
|
||||
// TODO: grant that name is also updated for mCurrentUpload.getOCUploadId
|
||||
|
||||
} else {
|
||||
removeResult = mPendingUploads.removePayload(
|
||||
|
@ -973,7 +1053,6 @@ public class FileUploader extends Service
|
|||
notifyUploadResult(mCurrentUpload, uploadResult);
|
||||
|
||||
sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second);
|
||||
|
||||
}
|
||||
|
||||
// generate new Thumbnail
|
||||
|
@ -997,8 +1076,7 @@ public class FileUploader extends Service
|
|||
private void notifyUploadStart(UploadFileOperation upload) {
|
||||
// / create status notification with a progress bar
|
||||
mLastPercent = 0;
|
||||
mNotificationBuilder =
|
||||
NotificationUtils.newNotificationBuilder(this);
|
||||
mNotificationBuilder = NotificationUtils.newNotificationBuilder(this);
|
||||
mNotificationBuilder
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
|
@ -1022,7 +1100,6 @@ public class FileUploader extends Service
|
|||
} // else wait until the upload really start (onTransferProgress is called), so that if it's discarded
|
||||
// due to lack of Wifi, no notification is shown
|
||||
// TODO generalize for automated uploads
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1058,7 +1135,8 @@ public class FileUploader extends Service
|
|||
if (!uploadResult.isCancelled() &&
|
||||
!ResultCode.LOCAL_FILE_NOT_FOUND.equals(uploadResult.getCode()) &&
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_FOR_WIFI) &&
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_FOR_CHARGING)) {
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_FOR_CHARGING) &&
|
||||
!uploadResult.getCode().equals(ResultCode.LOCK_FAILED) ) {
|
||||
|
||||
int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
|
||||
R.string.uploader_upload_failed_ticker;
|
||||
|
@ -1077,9 +1155,7 @@ public class FileUploader extends Service
|
|||
.setOngoing(false)
|
||||
.setProgress(0, 0, false);
|
||||
|
||||
content = ErrorMessageAdapter.getErrorCauseMessage(
|
||||
uploadResult, upload, getResources()
|
||||
);
|
||||
content = ErrorMessageAdapter.getErrorCauseMessage(uploadResult, upload, getResources());
|
||||
|
||||
if (needsToUpdateCredentials) {
|
||||
// let the user update credentials with one click
|
||||
|
@ -1130,7 +1206,6 @@ public class FileUploader extends Service
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a broadcast in order to the interested activities can update their
|
||||
* view
|
||||
|
@ -1144,7 +1219,6 @@ public class FileUploader extends Service
|
|||
sendStickyBroadcast(start);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a broadcast in order to the interested activities can update their
|
||||
* view
|
||||
|
@ -1208,5 +1282,4 @@ public class FileUploader extends Service
|
|||
mPendingUploads.remove(account.name);
|
||||
mUploadsStorageManager.removeUploads(account.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.services;
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
|
@ -76,7 +76,7 @@ public class AccountRemovalJob extends Job implements AccountManagerCallback<Boo
|
|||
|
||||
// remove pending account removal
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
|
||||
arbitraryDataProvider.deleteKeyForAccount(account, PENDING_FOR_REMOVAL);
|
||||
arbitraryDataProvider.deleteKeyForAccount(account.name, PENDING_FOR_REMOVAL);
|
||||
|
||||
return Result.SUCCESS;
|
||||
} else {
|
|
@ -19,7 +19,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.services;
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ComponentName;
|
||||
|
@ -44,6 +44,7 @@ import com.owncloud.android.datamodel.OCFile;
|
|||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.services.OperationsService;
|
||||
import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -97,7 +98,7 @@ public class ContactsBackupJob extends Job {
|
|||
OperationsService.BIND_AUTO_CREATE);
|
||||
|
||||
// store execution date
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
|
||||
ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP,
|
||||
String.valueOf(Calendar.getInstance().getTimeInMillis()));
|
||||
} else {
|
||||
|
@ -156,7 +157,9 @@ public class ContactsBackupJob extends Job {
|
|||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
null,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
|
@ -19,7 +19,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.services;
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
153
src/main/java/com/owncloud/android/jobs/FilesSyncJob.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.media.ExifInterface;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.evernote.android.job.Job;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.datamodel.FilesystemDataProvider;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
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;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/*
|
||||
Job that:
|
||||
- restarts existing jobs if required
|
||||
- finds new and modified files since we last run this
|
||||
- creates upload tasks
|
||||
*/
|
||||
public class FilesSyncJob extends Job {
|
||||
public static final String TAG = "FilesSyncJob";
|
||||
|
||||
public static final String SKIP_CUSTOM = "skipCustom";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Result onRunJob(Params params) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
|
||||
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
TAG);
|
||||
wakeLock.acquire();
|
||||
|
||||
PersistableBundleCompat bundle = params.getExtras();
|
||||
final boolean skipCustom = bundle.getBoolean(SKIP_CUSTOM, false);
|
||||
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
FilesSyncHelper.insertAllDBEntries(skipCustom);
|
||||
|
||||
// Create all the providers we'll need
|
||||
final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver);
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if ((syncedFolder.isEnabled()) && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.getType())) {
|
||||
for (String path : filesystemDataProvider.getFilesForUpload(syncedFolder.getLocalPath(),
|
||||
Long.toString(syncedFolder.getId()))) {
|
||||
File file = new File(path);
|
||||
|
||||
Long lastModificationTime = file.lastModified();
|
||||
final Locale currentLocale = context.getResources().getConfiguration().locale;
|
||||
|
||||
if (MediaFolderType.IMAGE == syncedFolder.getType()) {
|
||||
String mimetypeString = FileStorageUtils.getMimeTypeFromName(file.getAbsolutePath());
|
||||
if ("image/jpeg".equalsIgnoreCase(mimetypeString) || "image/tiff".
|
||||
equalsIgnoreCase(mimetypeString)) {
|
||||
try {
|
||||
ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
|
||||
String exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
|
||||
if (!TextUtils.isEmpty(exifDate)) {
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss",
|
||||
currentLocale);
|
||||
sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
|
||||
Date dateTime = sFormatter.parse(exifDate, pos);
|
||||
lastModificationTime = dateTime.getTime();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log_OC.d(TAG, "Failed to get the proper time " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean needsCharging = syncedFolder.getChargingOnly();
|
||||
boolean needsWifi = syncedFolder.getWifiOnly();
|
||||
|
||||
String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getAbsolutePath());
|
||||
|
||||
Account account = AccountUtils.getOwnCloudAccountByName(context, syncedFolder.getAccount());
|
||||
|
||||
requester.uploadFileWithOverwrite(
|
||||
context,
|
||||
account,
|
||||
file.getAbsolutePath(),
|
||||
FileStorageUtils.getInstantUploadFilePath(
|
||||
currentLocale,
|
||||
syncedFolder.getRemotePath(), file.getName(),
|
||||
lastModificationTime,
|
||||
syncedFolder.getSubfolderByDate()),
|
||||
syncedFolder.getUploadAction(),
|
||||
mimeType,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
|
||||
needsWifi,
|
||||
needsCharging,
|
||||
true
|
||||
);
|
||||
|
||||
filesystemDataProvider.updateFilesystemFileAsSentForUpload(path,
|
||||
Long.toString(syncedFolder.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wakeLock.release();
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.services;
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import com.evernote.android.job.Job;
|
||||
import com.evernote.android.job.JobCreator;
|
||||
|
@ -31,14 +31,14 @@ public class NCJobCreator implements JobCreator {
|
|||
@Override
|
||||
public Job create(String tag) {
|
||||
switch (tag) {
|
||||
case AutoUploadJob.TAG:
|
||||
return new AutoUploadJob();
|
||||
case ContactsBackupJob.TAG:
|
||||
return new ContactsBackupJob();
|
||||
case ContactsImportJob.TAG:
|
||||
return new ContactsImportJob();
|
||||
case AccountRemovalJob.TAG:
|
||||
return new AccountRemovalJob();
|
||||
case FilesSyncJob.TAG:
|
||||
return new FilesSyncJob();
|
||||
default:
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.jobs;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/*
|
||||
Job that triggers new FilesSyncJob in case new photo or video were detected
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public class NContentObserverJob extends JobService {
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (params.getJobId() == FilesSyncHelper.ContentSyncJobId && params.getTriggeredContentAuthorities()
|
||||
!= null && params.getTriggeredContentUris() != null
|
||||
&& params.getTriggeredContentUris().length > 0) {
|
||||
|
||||
PersistableBundleCompat persistableBundleCompat = new PersistableBundleCompat();
|
||||
persistableBundleCompat.putBoolean(FilesSyncJob.SKIP_CUSTOM, true);
|
||||
|
||||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setExecutionWindow(1, TimeUnit.SECONDS.toMillis(2))
|
||||
.setBackoffCriteria(TimeUnit.SECONDS.toMillis(5), JobRequest.BackoffPolicy.LINEAR)
|
||||
.setUpdateCurrent(false)
|
||||
.build()
|
||||
.schedule();
|
||||
}
|
||||
|
||||
FilesSyncHelper.scheduleNJobs(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
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.RemoteOperationResult;
|
||||
|
@ -41,17 +39,14 @@ public class MoveFileOperation extends SyncOperation {
|
|||
private String mTargetParentPath;
|
||||
|
||||
private OCFile mFile;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param srcPath Remote path of the {@link OCFile} to move.
|
||||
* @param targetParentPath Path to the folder where the file will be moved into.
|
||||
* @param account OwnCloud account containing both the file and the target folder
|
||||
*/
|
||||
public MoveFileOperation(String srcPath, String targetParentPath, Account account) {
|
||||
public MoveFileOperation(String srcPath, String targetParentPath) {
|
||||
mSrcPath = srcPath;
|
||||
mTargetParentPath = targetParentPath;
|
||||
if (!mTargetParentPath.endsWith(OCFile.PATH_SEPARATOR)) {
|
||||
|
@ -68,7 +63,7 @@ public class MoveFileOperation extends SyncOperation {
|
|||
*/
|
||||
@Override
|
||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
||||
RemoteOperationResult result = null;
|
||||
RemoteOperationResult result;
|
||||
|
||||
/// 1. check move validity
|
||||
if (mTargetParentPath.startsWith(mSrcPath)) {
|
||||
|
|
|
@ -203,7 +203,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
|
||||
if (result.isSuccess()) {
|
||||
// request for the synchronization of KEPT-IN-SYNC file contents
|
||||
startContentSynchronizations(mFilesToSyncContents, client);
|
||||
startContentSynchronizations(mFilesToSyncContents);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -453,12 +453,9 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
* on.
|
||||
*
|
||||
* @param filesToSyncContents Synchronization operations to execute.
|
||||
* @param client Interface to the remote ownCloud server.
|
||||
*/
|
||||
private void startContentSynchronizations(
|
||||
List<SynchronizeFileOperation> filesToSyncContents, OwnCloudClient client
|
||||
) {
|
||||
RemoteOperationResult contentsResult = null;
|
||||
private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents) {
|
||||
RemoteOperationResult contentsResult;
|
||||
for (SynchronizeFileOperation op: filesToSyncContents) {
|
||||
contentsResult = op.execute(mStorageManager, mContext); // async
|
||||
if (!contentsResult.isSuccess()) {
|
||||
|
@ -487,7 +484,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
|||
* the operation.
|
||||
*/
|
||||
private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
|
||||
RemoteOperationResult result = null;
|
||||
RemoteOperationResult result;
|
||||
|
||||
// remote request
|
||||
GetRemoteSharesForFileOperation operation =
|
||||
|
|
|
@ -469,19 +469,6 @@ 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}
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
/**
|
||||
* ownCloud Android client application
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* @author David A. Velasco
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* 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;
|
||||
|
@ -24,11 +23,12 @@ import android.accounts.Account;
|
|||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.Device;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.db.PreferenceManager;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
|
||||
|
@ -44,7 +44,6 @@ 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.MimeType;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
@ -52,14 +51,20 @@ import com.owncloud.android.utils.UriUtils;
|
|||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.RequestEntity;
|
||||
import org.lukhnos.nnio.file.Files;
|
||||
import org.lukhnos.nnio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.OverlappingFileLockException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
@ -94,6 +99,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
private boolean mForceOverwrite = false;
|
||||
private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
|
||||
private int mCreatedBy = CREATED_BY_USER;
|
||||
private boolean mOnWifiOnly = false;
|
||||
private boolean mWhileChargingOnly = false;
|
||||
|
||||
private boolean mWasRenamed = false;
|
||||
private long mOCUploadId = -1;
|
||||
|
@ -147,7 +154,9 @@ public class UploadFileOperation extends SyncOperation {
|
|||
boolean chunked,
|
||||
boolean forceOverwrite,
|
||||
int localBehaviour,
|
||||
Context context
|
||||
Context context,
|
||||
boolean onWifiOnly,
|
||||
boolean whileChargingOnly
|
||||
) {
|
||||
if (account == null) {
|
||||
throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation " + "creation");
|
||||
|
@ -171,6 +180,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
} else {
|
||||
mFile = file;
|
||||
}
|
||||
mOnWifiOnly = onWifiOnly;
|
||||
mWhileChargingOnly = whileChargingOnly;
|
||||
mRemotePath = upload.getRemotePath();
|
||||
mChunked = chunked;
|
||||
mForceOverwrite = forceOverwrite;
|
||||
|
@ -182,6 +193,14 @@ public class UploadFileOperation extends SyncOperation {
|
|||
mRemoteFolderToBeCreated = upload.isCreateRemoteFolder();
|
||||
}
|
||||
|
||||
public boolean getIsWifiRequired() {
|
||||
return mOnWifiOnly;
|
||||
}
|
||||
|
||||
public boolean getIsChargingRequired() {
|
||||
return mWhileChargingOnly;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
@ -237,7 +256,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
}
|
||||
|
||||
public int getCreatedBy () {
|
||||
public int getCreatedBy() {
|
||||
return mCreatedBy;
|
||||
}
|
||||
|
||||
|
@ -249,9 +268,10 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return mCreatedBy == CREATED_AS_INSTANT_VIDEO;
|
||||
}
|
||||
|
||||
public void setOCUploadId(long id){
|
||||
public void setOCUploadId(long id) {
|
||||
mOCUploadId = id;
|
||||
}
|
||||
|
||||
public long getOCUploadId() {
|
||||
return mOCUploadId;
|
||||
}
|
||||
|
@ -260,14 +280,14 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return mDataTransferListeners;
|
||||
}
|
||||
|
||||
public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
|
||||
public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
|
||||
synchronized (mDataTransferListeners) {
|
||||
mDataTransferListeners.add(listener);
|
||||
}
|
||||
if (mEntity != null) {
|
||||
((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
|
||||
((ProgressiveDataTransferer) mEntity).addDatatransferProgressListener(listener);
|
||||
}
|
||||
if(mUploadOperation != null){
|
||||
if (mUploadOperation != null) {
|
||||
mUploadOperation.addDatatransferProgressListener(listener);
|
||||
}
|
||||
}
|
||||
|
@ -277,14 +297,14 @@ public class UploadFileOperation extends SyncOperation {
|
|||
mDataTransferListeners.remove(listener);
|
||||
}
|
||||
if (mEntity != null) {
|
||||
((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
|
||||
((ProgressiveDataTransferer) mEntity).removeDatatransferProgressListener(listener);
|
||||
}
|
||||
if(mUploadOperation != null){
|
||||
if (mUploadOperation != null) {
|
||||
mUploadOperation.removeDatatransferProgressListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void addRenameUploadListener (OnRenameListener listener) {
|
||||
public void addRenameUploadListener(OnRenameListener listener) {
|
||||
mRenameUploadListener = listener;
|
||||
}
|
||||
|
||||
|
@ -297,17 +317,18 @@ public class UploadFileOperation extends SyncOperation {
|
|||
File temporalFile = null;
|
||||
File originalFile = new File(mOriginalStoragePath);
|
||||
File expectedFile = null;
|
||||
FileLock fileLock = null;
|
||||
|
||||
try {
|
||||
|
||||
/// Check that connectivity conditions are met and delays the upload otherwise
|
||||
if (delayForWifi()) {
|
||||
if (mOnWifiOnly && !Device.getNetworkType(mContext).equals(JobRequest.NetworkType.UNMETERED)) {
|
||||
Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath());
|
||||
return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI);
|
||||
}
|
||||
|
||||
// Check if charging conditions are met and delays the upload otherwise
|
||||
if (delayForCharging()){
|
||||
if (mWhileChargingOnly && !Device.isCharging(mContext)) {
|
||||
Log_OC.d(TAG, "Upload delayed until the device is charging: " + getRemotePath());
|
||||
return new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING);
|
||||
}
|
||||
|
@ -372,20 +393,21 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
|
||||
// Get the last modification date of the file from the file system
|
||||
Long timeStampLong = originalFile.lastModified()/1000;
|
||||
Long timeStampLong = originalFile.lastModified() / 1000;
|
||||
String timeStamp = timeStampLong.toString();
|
||||
|
||||
/// perform the upload
|
||||
if ( mChunked &&
|
||||
if (mChunked &&
|
||||
(new File(mFile.getStoragePath())).length() >
|
||||
ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
|
||||
ChunkedUploadRemoteFileOperation.CHUNK_SIZE) {
|
||||
mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, mFile.getStoragePath(),
|
||||
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
|
||||
} else {
|
||||
mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
|
||||
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp);
|
||||
}
|
||||
Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
|
||||
|
||||
Iterator<OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
|
||||
while (listener.hasNext()) {
|
||||
mUploadOperation.addDatatransferProgressListener(listener.next());
|
||||
}
|
||||
|
@ -394,23 +416,77 @@ public class UploadFileOperation extends SyncOperation {
|
|||
throw new OperationCancelledException();
|
||||
}
|
||||
|
||||
FileChannel channel = null;
|
||||
try {
|
||||
channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel();
|
||||
fileLock = channel.tryLock();
|
||||
} catch (FileNotFoundException e) {
|
||||
if (temporalFile == null) {
|
||||
String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
|
||||
mFile.setStoragePath(temporalPath);
|
||||
temporalFile = new File(temporalPath);
|
||||
|
||||
result = copy(originalFile, temporalFile);
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
if (temporalFile.length() == originalFile.length()) {
|
||||
channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
|
||||
fileLock = channel.tryLock();
|
||||
} else {
|
||||
while (temporalFile.length() != originalFile.length()) {
|
||||
Files.deleteIfExists(Paths.get(temporalPath));
|
||||
result = copy(originalFile, temporalFile);
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").
|
||||
getChannel();
|
||||
fileLock = channel.tryLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel();
|
||||
fileLock = channel.tryLock();
|
||||
}
|
||||
}
|
||||
|
||||
result = mUploadOperation.execute(client);
|
||||
|
||||
/// move local temporal file or original file to its corresponding
|
||||
// location in the ownCloud local folder
|
||||
if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
|
||||
if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) {
|
||||
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore");
|
||||
result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
|
||||
} catch (OverlappingFileLockException e) {
|
||||
Log_OC.d(TAG, "Overlapping file lock exception");
|
||||
result = new RemoteOperationResult(ResultCode.LOCK_FAILED);
|
||||
} catch (Exception e) {
|
||||
result = new RemoteOperationResult(e);
|
||||
|
||||
} finally {
|
||||
mUploadStarted.set(false);
|
||||
|
||||
if (fileLock != null) {
|
||||
try {
|
||||
fileLock.release();
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Failed to unlock file with path " + mOriginalStoragePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (temporalFile != null && !originalFile.equals(temporalFile)) {
|
||||
temporalFile.delete();
|
||||
}
|
||||
if (result == null){
|
||||
if (result == null) {
|
||||
result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
|
||||
}
|
||||
if (result.isSuccess()) {
|
||||
|
@ -418,7 +494,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
result.getLogMessage());
|
||||
} else {
|
||||
if (result.getException() != null) {
|
||||
if(result.isCancelled()){
|
||||
if (result.isCancelled()) {
|
||||
Log_OC.w(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
|
||||
": " + result.getLogMessage());
|
||||
} else {
|
||||
|
@ -478,41 +554,6 @@ public class UploadFileOperation extends SyncOperation {
|
|||
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() && PreferenceManager.instantPictureUploadViaWiFiOnly(mContext)
|
||||
);
|
||||
boolean delayInstantVideo = (
|
||||
isInstantVideo() && PreferenceManager.instantVideoUploadViaWiFiOnly(mContext)
|
||||
);
|
||||
return (
|
||||
(delayInstantPicture || delayInstantVideo) &&
|
||||
!ConnectivityUtils.isAppConnectedViaUnmeteredWiFi(mContext)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if upload should be delayed due to not charging
|
||||
*
|
||||
* @return 'True' if the upload was delayed until device is charging, 'false' otherwise.
|
||||
*/
|
||||
private boolean delayForCharging() {
|
||||
boolean delayInstantPicture = isInstantPicture() &&
|
||||
PreferenceManager.instantPictureUploadWhenChargingOnly(mContext);
|
||||
|
||||
boolean delayInstantVideo = isInstantVideo() && PreferenceManager.instantVideoUploadWhenChargingOnly(mContext);
|
||||
|
||||
return ((delayInstantPicture || delayInstantVideo) && !ConnectivityUtils.isCharging(mContext));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the existence of the folder where the current file will be uploaded both
|
||||
* in the remote server and in the local database.
|
||||
|
@ -566,7 +607,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
|
||||
/**
|
||||
* Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false.
|
||||
* New file is stored as mFile, original as mOldFile.
|
||||
* New file is stored as mFile, original as mOldFile.
|
||||
*
|
||||
* @param newRemotePath new remote path
|
||||
*/
|
||||
private void createNewOCFile(String newRemotePath) {
|
||||
|
@ -615,8 +657,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
suffix = " (" + count + ")";
|
||||
if (pos >= 0) {
|
||||
check = existsFile(wc, remotePath + suffix + "." + extension);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
check = existsFile(wc, remotePath + suffix);
|
||||
}
|
||||
count++;
|
||||
|
@ -629,7 +670,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean existsFile(OwnCloudClient client, String remotePath){
|
||||
private boolean existsFile(OwnCloudClient client, String remotePath) {
|
||||
ExistenceCheckRemoteOperation existsOperation =
|
||||
new ExistenceCheckRemoteOperation(remotePath, mContext, false);
|
||||
RemoteOperationResult result = existsOperation.execute(client);
|
||||
|
@ -667,10 +708,10 @@ public class UploadFileOperation extends SyncOperation {
|
|||
* 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
|
||||
* @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");
|
||||
|
@ -758,10 +799,10 @@ public class UploadFileOperation extends SyncOperation {
|
|||
*
|
||||
* 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
|
||||
* @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 {
|
||||
|
||||
|
@ -769,8 +810,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
File expectedFolder = targetFile.getParentFile();
|
||||
expectedFolder.mkdirs();
|
||||
|
||||
if (expectedFolder.isDirectory()){
|
||||
if (!sourceFile.renameTo(targetFile)){
|
||||
if (expectedFolder.isDirectory()) {
|
||||
if (!sourceFile.renameTo(targetFile)) {
|
||||
// try to copy and then delete
|
||||
targetFile.createNewFile();
|
||||
FileChannel inChannel = new FileInputStream(sourceFile).getChannel();
|
||||
|
@ -778,12 +819,11 @@ public class UploadFileOperation extends SyncOperation {
|
|||
try {
|
||||
inChannel.transferTo(0, inChannel.size(), outChannel);
|
||||
sourceFile.delete();
|
||||
} catch (Exception e){
|
||||
} 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 {
|
||||
} finally {
|
||||
if (inChannel != null) {
|
||||
inChannel.close();
|
||||
}
|
||||
|
@ -847,7 +887,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
|
||||
// generate new Thumbnail
|
||||
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
||||
new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount);
|
||||
new ThumbnailsCacheManager.ThumbnailGenerationTask(getStorageManager(), mAccount);
|
||||
task.execute(file);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package org.nextcloud.providers;
|
||||
package com.owncloud.android.providers;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.TargetApi;
|
|
@ -74,6 +74,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
private static final int EXTERNAL_LINKS = 8;
|
||||
private static final int ARBITRARY_DATA = 9;
|
||||
private static final int VIRTUAL = 10;
|
||||
private static final int FILESYSTEM = 11;
|
||||
|
||||
private static final String TAG = FileContentProvider.class.getSimpleName();
|
||||
|
||||
|
@ -209,6 +210,9 @@ public class FileContentProvider extends ContentProvider {
|
|||
case VIRTUAL:
|
||||
count = db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs);
|
||||
break;
|
||||
case FILESYSTEM:
|
||||
count = db.delete(ProviderTableMeta.FILESYSTEM_TABLE_NAME, where, whereArgs);
|
||||
break;
|
||||
default:
|
||||
//Log_OC.e(TAG, "Unknown uri " + uri);
|
||||
throw new IllegalArgumentException("Unknown uri: " + uri.toString());
|
||||
|
@ -312,7 +316,6 @@ public class FileContentProvider extends ContentProvider {
|
|||
if (uploadId > 0) {
|
||||
insertedUploadUri =
|
||||
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_UPLOADS, uploadId);
|
||||
trimSuccessfulUploads(db);
|
||||
} else {
|
||||
throw new SQLException(ERROR + uri);
|
||||
|
||||
|
@ -365,6 +368,16 @@ public class FileContentProvider extends ContentProvider {
|
|||
}
|
||||
|
||||
return insertedVirtualUri;
|
||||
case FILESYSTEM:
|
||||
Uri insertedFilesystemUri = null;
|
||||
long filesystedId = db.insert(ProviderTableMeta.FILESYSTEM_TABLE_NAME, null, values);
|
||||
if (filesystedId > 0) {
|
||||
insertedFilesystemUri =
|
||||
ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILESYSTEM, filesystedId);
|
||||
} else {
|
||||
throw new SQLException("ERROR " + uri);
|
||||
}
|
||||
return insertedFilesystemUri;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown uri id: " + uri);
|
||||
}
|
||||
|
@ -417,6 +430,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
mUriMatcher.addURI(authority, "external_links", EXTERNAL_LINKS);
|
||||
mUriMatcher.addURI(authority, "arbitrary_data", ARBITRARY_DATA);
|
||||
mUriMatcher.addURI(authority, "virtual", VIRTUAL);
|
||||
mUriMatcher.addURI(authority, "filesystem", FILESYSTEM);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -518,6 +532,13 @@ public class FileContentProvider extends ContentProvider {
|
|||
sqlQuery.appendWhere(ProviderTableMeta._ID + "=" + uri.getPathSegments().get(1));
|
||||
}
|
||||
break;
|
||||
case FILESYSTEM:
|
||||
sqlQuery.setTables(ProviderTableMeta.FILESYSTEM_TABLE_NAME);
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
sqlQuery.appendWhere(ProviderTableMeta._ID + "="
|
||||
+ uri.getPathSegments().get(1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown uri id: " + uri);
|
||||
}
|
||||
|
@ -549,6 +570,9 @@ public class FileContentProvider extends ContentProvider {
|
|||
default: // Files
|
||||
order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
|
||||
break;
|
||||
case FILESYSTEM:
|
||||
order = ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
order = sortOrder;
|
||||
|
@ -594,12 +618,13 @@ 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;
|
||||
case SYNCED_FOLDERS:
|
||||
return db.update(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, values, selection, selectionArgs);
|
||||
case ARBITRARY_DATA:
|
||||
return db.update(ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME, values, selection, selectionArgs);
|
||||
case FILESYSTEM:
|
||||
return db.update(ProviderTableMeta.FILESYSTEM_TABLE_NAME, values, selection, selectionArgs);
|
||||
default:
|
||||
return db.update(ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs);
|
||||
}
|
||||
|
@ -662,6 +687,9 @@ public class FileContentProvider extends ContentProvider {
|
|||
|
||||
// Create virtual table
|
||||
createVirtualTable(db);
|
||||
|
||||
// Create filesystem table
|
||||
createFileSystemTable(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -669,14 +697,14 @@ public class FileContentProvider extends ContentProvider {
|
|||
Log_OC.i(SQL, "Entering in onUpgrade");
|
||||
boolean upgraded = false;
|
||||
if (oldVersion == 1 && newVersion >= 2) {
|
||||
Log_OC.i(SQL, "Entering in the #1 ADD in onUpgrade");
|
||||
Log_OC.i(SQL, "Entering in the #2 ADD in onUpgrade");
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
|
||||
" DEFAULT 0");
|
||||
upgraded = true;
|
||||
}
|
||||
if (oldVersion < 3 && newVersion >= 3) {
|
||||
Log_OC.i(SQL, "Entering in the #2 ADD in onUpgrade");
|
||||
Log_OC.i(SQL, "Entering in the #3 ADD in onUpgrade");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
|
@ -696,7 +724,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
}
|
||||
}
|
||||
if (oldVersion < 4 && newVersion >= 4) {
|
||||
Log_OC.i(SQL, "Entering in the #3 ADD in onUpgrade");
|
||||
Log_OC.i(SQL, "Entering in the #4 ADD in onUpgrade");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
|
@ -720,7 +748,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
}
|
||||
|
||||
if (oldVersion < 5 && newVersion >= 5) {
|
||||
Log_OC.i(SQL, "Entering in the #4 ADD in onUpgrade");
|
||||
Log_OC.i(SQL, "Entering in the #5 ADD in onUpgrade");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
|
@ -738,7 +766,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
}
|
||||
|
||||
if (oldVersion < 6 && newVersion >= 6) {
|
||||
Log_OC.i(SQL, "Entering in the #5 ADD in onUpgrade");
|
||||
Log_OC.i(SQL, "Entering in the #6 ADD in onUpgrade");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
|
||||
|
@ -843,6 +871,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
|
@ -937,7 +966,6 @@ public class FileContentProvider extends ContentProvider {
|
|||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!upgraded) {
|
||||
|
@ -1033,6 +1061,43 @@ public class FileContentProvider extends ContentProvider {
|
|||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
|
||||
if (oldVersion < 23 && newVersion >= 23) {
|
||||
Log_OC.i(SQL, "Entering in the #23 adding type column for synced folders, Create filesystem table");
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// add type column default being CUSTOM (0)
|
||||
Log_OC.i(SQL, "Add type column and default value 0 (CUSTOM) to synced_folders table");
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE +
|
||||
" INTEGER " + " DEFAULT 0");
|
||||
|
||||
Log_OC.i(SQL, "Add charging and wifi columns to uploads");
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.UPLOADS_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.UPLOADS_IS_WIFI_ONLY +
|
||||
" INTEGER " + " DEFAULT 0");
|
||||
|
||||
db.execSQL(ALTER_TABLE + ProviderTableMeta.UPLOADS_TABLE_NAME +
|
||||
ADD_COLUMN + ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY +
|
||||
" INTEGER " + " DEFAULT 0");
|
||||
|
||||
// create Filesystem table
|
||||
Log_OC.i(SQL, "Create filesystem table");
|
||||
createFileSystemTable(db);
|
||||
|
||||
upgraded = true;
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
} catch (Throwable t) {
|
||||
Log_OC.e(TAG, "ERROR!", t);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
if (!upgraded) {
|
||||
Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1135,6 +1200,8 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + INTEGER
|
||||
+ ProviderTableMeta.UPLOADS_LAST_RESULT + INTEGER // Upload LastResult
|
||||
+ ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_IS_WIFI_ONLY + INTEGER // boolean
|
||||
+ ProviderTableMeta.UPLOADS_CREATED_BY + " INTEGER );" // Upload createdBy
|
||||
);
|
||||
|
||||
|
@ -1159,7 +1226,8 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER );" // upload action
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, " // upload action
|
||||
+ ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER );" // type
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1170,7 +1238,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE + " TEXT, " // language
|
||||
+ ProviderTableMeta.EXTERNAL_LINKS_TYPE + " INTEGER, " // type
|
||||
+ ProviderTableMeta.EXTERNAL_LINKS_NAME + " TEXT, " // name
|
||||
+ ProviderTableMeta.EXTERNAL_LINKS_URL + " TEXT )" // url
|
||||
+ ProviderTableMeta.EXTERNAL_LINKS_URL + " TEXT );" // url
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1179,7 +1247,7 @@ public class FileContentProvider extends ContentProvider {
|
|||
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " // id
|
||||
+ ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " TEXT, " // cloud id (account name + FQDN)
|
||||
+ ProviderTableMeta.ARBITRARY_DATA_KEY + " TEXT, " // key
|
||||
+ ProviderTableMeta.ARBITRARY_DATA_VALUE + " TEXT) " // value
|
||||
+ ProviderTableMeta.ARBITRARY_DATA_VALUE + " TEXT );" // value
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1191,6 +1259,19 @@ public class FileContentProvider extends ContentProvider {
|
|||
);
|
||||
}
|
||||
|
||||
private void createFileSystemTable(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE " + ProviderTableMeta.FILESYSTEM_TABLE_NAME + "("
|
||||
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " // id
|
||||
+ ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " TEXT, "
|
||||
+ ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " INTEGER, "
|
||||
+ ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY + " LONG, "
|
||||
+ ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " INTEGER, "
|
||||
+ ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " STRING, "
|
||||
+ ProviderTableMeta.FILESYSTEM_FILE_MODIFIED + " LONG );"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -1,237 +0,0 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* <p>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
* <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 Affero General Public License for more details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.media.ExifInterface;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.activity.Preferences;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import org.apache.commons.io.monitor.FileAlterationListener;
|
||||
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Magical file alteration listener
|
||||
*/
|
||||
|
||||
public class AdvancedFileAlterationListener implements FileAlterationListener {
|
||||
|
||||
public static final String TAG = "AdvancedFileAlterationListener";
|
||||
public static final int DELAY_INVOCATION_MS = 2500;
|
||||
private Context context;
|
||||
private boolean lightVersion;
|
||||
|
||||
private SyncedFolder syncedFolder;
|
||||
|
||||
private Map<String, Runnable> uploadMap = new HashMap<>();
|
||||
private Handler handler = new Handler();
|
||||
|
||||
public AdvancedFileAlterationListener(SyncedFolder syncedFolder, boolean lightVersion) {
|
||||
super();
|
||||
|
||||
context = MainApp.getAppContext();
|
||||
this.lightVersion = lightVersion;
|
||||
this.syncedFolder = syncedFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(FileAlterationObserver observer) {
|
||||
// This method is intentionally empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDirectoryCreate(File directory) {
|
||||
// This method is intentionally empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDirectoryChange(File directory) {
|
||||
// This method is intentionally empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDirectoryDelete(File directory) {
|
||||
// This method is intentionally empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileCreate(final File file) {
|
||||
onFileCreate(file, DELAY_INVOCATION_MS);
|
||||
}
|
||||
|
||||
public void onFileCreate(final File file, int delay) {
|
||||
if (file != null) {
|
||||
uploadMap.put(file.getAbsolutePath(), null);
|
||||
|
||||
String mimetypeString = FileStorageUtils.getMimeTypeFromName(file.getAbsolutePath());
|
||||
Long lastModificationTime = file.lastModified();
|
||||
final Locale currentLocale = context.getResources().getConfiguration().locale;
|
||||
|
||||
if ("image/jpeg".equalsIgnoreCase(mimetypeString) || "image/tiff".equalsIgnoreCase(mimetypeString)) {
|
||||
try {
|
||||
ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
|
||||
String exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
|
||||
if (!TextUtils.isEmpty(exifDate)) {
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale);
|
||||
sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
|
||||
Date dateTime = sFormatter.parse(exifDate, pos);
|
||||
lastModificationTime = dateTime.getTime();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log_OC.d(TAG, "Failed to get the proper time " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Long finalLastModificationTime = lastModificationTime;
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
String remotePath;
|
||||
boolean subfolderByDate;
|
||||
boolean chargingOnly;
|
||||
boolean wifiOnly;
|
||||
Integer uploadAction;
|
||||
String accountName = syncedFolder.getAccount();
|
||||
|
||||
if (lightVersion) {
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context
|
||||
.getContentResolver());
|
||||
|
||||
Resources resources = MainApp.getAppContext().getResources();
|
||||
|
||||
remotePath = resources.getString(R.string.syncedFolder_remote_folder) + OCFile.PATH_SEPARATOR +
|
||||
new File(syncedFolder.getLocalPath()).getName() + OCFile.PATH_SEPARATOR;
|
||||
subfolderByDate = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders);
|
||||
chargingOnly = resources.getBoolean(R.bool.syncedFolder_light_on_charging);
|
||||
wifiOnly = arbitraryDataProvider.getBooleanValue(accountName,
|
||||
Preferences.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI);
|
||||
String uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour);
|
||||
uploadAction = getUploadAction(uploadActionString);
|
||||
} else {
|
||||
remotePath = syncedFolder.getRemotePath();
|
||||
subfolderByDate = syncedFolder.getSubfolderByDate();
|
||||
chargingOnly = syncedFolder.getChargingOnly();
|
||||
wifiOnly = syncedFolder.getWifiOnly();
|
||||
uploadAction = syncedFolder.getUploadAction();
|
||||
}
|
||||
|
||||
PersistableBundleCompat bundle = new PersistableBundleCompat();
|
||||
bundle.putString(AutoUploadJob.LOCAL_PATH, file.getAbsolutePath());
|
||||
bundle.putString(AutoUploadJob.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath(
|
||||
currentLocale,
|
||||
remotePath, file.getName(),
|
||||
finalLastModificationTime,
|
||||
subfolderByDate));
|
||||
bundle.putString(AutoUploadJob.ACCOUNT, accountName);
|
||||
bundle.putInt(AutoUploadJob.UPLOAD_BEHAVIOUR, uploadAction);
|
||||
|
||||
new JobRequest.Builder(AutoUploadJob.TAG)
|
||||
.setExecutionWindow(30_000L, 80_000L)
|
||||
.setRequiresCharging(chargingOnly)
|
||||
.setRequiredNetworkType(wifiOnly ? JobRequest.NetworkType.UNMETERED :
|
||||
JobRequest.NetworkType.ANY)
|
||||
.setExtras(bundle)
|
||||
.setPersisted(false)
|
||||
.setRequirementsEnforced(true)
|
||||
.setUpdateCurrent(false)
|
||||
.build()
|
||||
.schedule();
|
||||
|
||||
uploadMap.remove(file.getAbsolutePath());
|
||||
}
|
||||
};
|
||||
|
||||
uploadMap.put(file.getAbsolutePath(), runnable);
|
||||
handler.postDelayed(runnable, delay);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getUploadAction(String action) {
|
||||
switch (action) {
|
||||
case "LOCAL_BEHAVIOUR_FORGET":
|
||||
return FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
case "LOCAL_BEHAVIOUR_MOVE":
|
||||
return FileUploader.LOCAL_BEHAVIOUR_MOVE;
|
||||
case "LOCAL_BEHAVIOUR_DELETE":
|
||||
return FileUploader.LOCAL_BEHAVIOUR_DELETE;
|
||||
default:
|
||||
return FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileChange(File file) {
|
||||
onFileChange(file, 2500);
|
||||
}
|
||||
|
||||
public void onFileChange(File file, int delay) {
|
||||
Runnable runnable;
|
||||
if ((runnable = uploadMap.get(file.getAbsolutePath())) != null) {
|
||||
handler.removeCallbacks(runnable);
|
||||
handler.postDelayed(runnable, delay);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileDelete(File file) {
|
||||
Runnable runnable;
|
||||
if ((runnable = uploadMap.get(file.getAbsolutePath())) != null) {
|
||||
handler.removeCallbacks(runnable);
|
||||
uploadMap.remove(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(FileAlterationObserver observer) {
|
||||
// This method is intentionally empty
|
||||
}
|
||||
|
||||
public int getActiveTasksCount() {
|
||||
return uploadMap.size();
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2016 Tobias Kaminsky
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* <p>
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* <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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.services;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.evernote.android.job.Job;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class AutoUploadJob extends Job {
|
||||
public static final String TAG = "AutoUploadJob";
|
||||
|
||||
public static final String LOCAL_PATH = "filePath";
|
||||
public static final String REMOTE_PATH = "remotePath";
|
||||
public static final String ACCOUNT = "account";
|
||||
public static final String UPLOAD_BEHAVIOUR = "uploadBehaviour";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Result onRunJob(Params params) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
PersistableBundleCompat bundle = params.getExtras();
|
||||
final String filePath = bundle.getString(LOCAL_PATH, "");
|
||||
final String remotePath = bundle.getString(REMOTE_PATH, "");
|
||||
final Account account = AccountUtils.getOwnCloudAccountByName(context, bundle.getString(ACCOUNT, ""));
|
||||
final Integer uploadBehaviour = bundle.getInt(UPLOAD_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_FORGET);
|
||||
|
||||
|
||||
File file = new File(filePath);
|
||||
|
||||
|
||||
// File can be deleted between job generation and job execution. If file does not exist, just ignore it
|
||||
if (file.exists()) {
|
||||
final String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getAbsolutePath());
|
||||
|
||||
final FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.uploadNewFile(
|
||||
context,
|
||||
account,
|
||||
filePath,
|
||||
remotePath,
|
||||
uploadBehaviour,
|
||||
mimeType,
|
||||
true, // create parent folder if not existent
|
||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
}
|
|
@ -678,7 +678,7 @@ public class OperationsService extends Service {
|
|||
// Move file/folder
|
||||
String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
String newParentPath = operationIntent.getStringExtra(EXTRA_NEW_PARENT_PATH);
|
||||
operation = new MoveFileOperation(remotePath, newParentPath, account);
|
||||
operation = new MoveFileOperation(remotePath, newParentPath);
|
||||
|
||||
} else if (action.equals(ACTION_COPY_FILE)) {
|
||||
// Copy file/folder
|
||||
|
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Original source code:
|
||||
* https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
|
||||
*
|
||||
* Modified by Mario Danic
|
||||
* Changes are Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud GmbH
|
||||
*
|
||||
* All changes are under the same licence as the original.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.services.observer;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.AdvancedFileAlterationListener;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.comparator.NameFileComparator;
|
||||
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||
import org.apache.commons.io.monitor.FileEntry;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class AdvancedFileAlterationObserver extends FileAlterationObserver implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1185122225658782848L;
|
||||
private static final int DELAY_INVOCATION_MS = 2500;
|
||||
private final List<AdvancedFileAlterationListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private FileEntry rootEntry;
|
||||
private FileFilter fileFilter;
|
||||
private Comparator<File> comparator;
|
||||
private SyncedFolder syncedFolder;
|
||||
|
||||
private static final FileEntry[] EMPTY_ENTRIES = new FileEntry[0];
|
||||
|
||||
public AdvancedFileAlterationObserver(SyncedFolder syncedFolder, FileFilter fileFilter) {
|
||||
super(syncedFolder.getLocalPath(), fileFilter);
|
||||
|
||||
this.rootEntry = new FileEntry(new File(syncedFolder.getLocalPath()));
|
||||
this.fileFilter = fileFilter;
|
||||
this.syncedFolder = syncedFolder;
|
||||
comparator = NameFileComparator.NAME_SYSTEM_COMPARATOR;
|
||||
}
|
||||
|
||||
public long getSyncedFolderID() {
|
||||
return syncedFolder.getId();
|
||||
}
|
||||
|
||||
public SyncedFolder getSyncedFolder() {
|
||||
return syncedFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the directory being observed.
|
||||
*
|
||||
* @return the directory being observed
|
||||
*/
|
||||
public File getDirectory() {
|
||||
return rootEntry.getFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fileFilter.
|
||||
*
|
||||
* @return the fileFilter
|
||||
* @since 2.1
|
||||
*/
|
||||
public FileFilter getFileFilter() {
|
||||
return fileFilter;
|
||||
}
|
||||
|
||||
public FileEntry getRootEntry() {
|
||||
return rootEntry;
|
||||
}
|
||||
|
||||
public void setRootEntry(FileEntry rootEntry) {
|
||||
this.rootEntry = rootEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file system listener.
|
||||
*
|
||||
* @param listener The file system listener
|
||||
*/
|
||||
public void addListener(final AdvancedFileAlterationListener listener) {
|
||||
if (listener != null) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file system listener.
|
||||
*
|
||||
* @param listener The file system listener
|
||||
*/
|
||||
public void removeListener(final AdvancedFileAlterationListener listener) {
|
||||
if (listener != null) {
|
||||
while (listeners.remove(listener)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of registered file system listeners.
|
||||
*
|
||||
* @return The file system listeners
|
||||
*/
|
||||
public Iterable<AdvancedFileAlterationListener> getMagicListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing - hack for the monitor
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void initialize() {
|
||||
// does nothing - hack the monitor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes everything
|
||||
*
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
public void init() throws Exception {
|
||||
rootEntry.refresh(rootEntry.getFile());
|
||||
final FileEntry[] children = doListFiles(rootEntry.getFile(), rootEntry);
|
||||
rootEntry.setChildren(children);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Final processing.
|
||||
*
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
public void destroy() throws Exception {
|
||||
Iterator iterator = getMagicListeners().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
AdvancedFileAlterationListener AdvancedFileAlterationListener = (AdvancedFileAlterationListener) iterator.next();
|
||||
while (AdvancedFileAlterationListener.getActiveTasksCount() > 0) {
|
||||
SystemClock.sleep(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAndNotifyNow() {
|
||||
/* fire onStart() */
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
listener.onStart(this);
|
||||
}
|
||||
|
||||
/* fire directory/file events */
|
||||
final File rootFile = rootEntry.getFile();
|
||||
if (rootFile.exists()) {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile), 0);
|
||||
} else if (rootEntry.isExists()) {
|
||||
try {
|
||||
// try to init once more
|
||||
init();
|
||||
if (rootEntry.getFile().exists()) {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootEntry.getFile()), 0);
|
||||
} else {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, 0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.d("AdvancedFileAlterationObserver", "Failed getting an observer to intialize " + e);
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, 0);
|
||||
}
|
||||
} // else didn't exist and still doesn't
|
||||
|
||||
/* fire onStop() */
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
listener.onStop(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the file and its children have been created, modified or deleted.
|
||||
*/
|
||||
public void checkAndNotify() {
|
||||
|
||||
/* fire onStart() */
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
listener.onStart(this);
|
||||
}
|
||||
|
||||
/* fire directory/file events */
|
||||
final File rootFile = rootEntry.getFile();
|
||||
if (rootFile.exists()) {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile), DELAY_INVOCATION_MS);
|
||||
} else if (rootEntry.isExists()) {
|
||||
try {
|
||||
// try to init once more
|
||||
init();
|
||||
if (rootEntry.getFile().exists()) {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootEntry.getFile()),
|
||||
DELAY_INVOCATION_MS);
|
||||
} else {
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, DELAY_INVOCATION_MS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.d("AdvancedFileAlterationObserver", "Failed getting an observer to intialize " + e);
|
||||
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, DELAY_INVOCATION_MS);
|
||||
}
|
||||
} // else didn't exist and still doesn't
|
||||
|
||||
/* fire onStop() */
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
listener.onStop(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two file lists for files which have been created, modified or deleted.
|
||||
*
|
||||
* @param parent The parent entry
|
||||
* @param previous The original list of files
|
||||
* @param files The current list of files
|
||||
*/
|
||||
private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files, int delay) {
|
||||
if (files != null && files.length > 0) {
|
||||
int c = 0;
|
||||
final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES;
|
||||
for (final FileEntry entry : previous) {
|
||||
while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) {
|
||||
current[c] = createFileEntry(parent, files[c]);
|
||||
doCreate(current[c], delay);
|
||||
c++;
|
||||
}
|
||||
if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) {
|
||||
doMatch(entry, files[c], delay);
|
||||
checkAndNotify(entry, entry.getChildren(), listFiles(files[c]), delay);
|
||||
current[c] = entry;
|
||||
c++;
|
||||
} else {
|
||||
checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, delay);
|
||||
doDelete(entry);
|
||||
}
|
||||
}
|
||||
for (; c < files.length; c++) {
|
||||
current[c] = createFileEntry(parent, files[c]);
|
||||
doCreate(current[c], delay);
|
||||
}
|
||||
parent.setChildren(current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new file entry for the specified file.
|
||||
*
|
||||
* @param parent The parent file entry
|
||||
* @param file The file to create an entry for
|
||||
* @return A new file entry
|
||||
*/
|
||||
private FileEntry createFileEntry(final FileEntry parent, final File file) {
|
||||
final FileEntry entry = parent.newChildInstance(file);
|
||||
entry.refresh(file);
|
||||
final FileEntry[] children = doListFiles(file, entry);
|
||||
entry.setChildren(children);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files
|
||||
*
|
||||
* @param file The file to list files for
|
||||
* @param entry the parent entry
|
||||
* @return The child files
|
||||
*/
|
||||
private FileEntry[] doListFiles(File file, FileEntry entry) {
|
||||
final File[] files = listFiles(file);
|
||||
final FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES;
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
children[i] = createFileEntry(entry, files[i]);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire directory/file created events to the registered listeners.
|
||||
*
|
||||
* @param entry The file entry
|
||||
*/
|
||||
private void doCreate(final FileEntry entry, int delay) {
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
if (entry.isDirectory()) {
|
||||
listener.onDirectoryCreate(entry.getFile());
|
||||
} else {
|
||||
listener.onFileCreate(entry.getFile(), delay);
|
||||
}
|
||||
}
|
||||
final FileEntry[] children = entry.getChildren();
|
||||
for (final FileEntry aChildren : children) {
|
||||
doCreate(aChildren, delay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire directory/file change events to the registered listeners.
|
||||
*
|
||||
* @param entry The previous file system entry
|
||||
* @param file The current file
|
||||
*/
|
||||
private void doMatch(final FileEntry entry, final File file, int delay) {
|
||||
if (entry.refresh(file)) {
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
if (entry.isDirectory()) {
|
||||
listener.onDirectoryChange(file);
|
||||
} else {
|
||||
listener.onFileChange(file, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire directory/file delete events to the registered listeners.
|
||||
*
|
||||
* @param entry The file entry
|
||||
*/
|
||||
private void doDelete(final FileEntry entry) {
|
||||
for (final AdvancedFileAlterationListener listener : listeners) {
|
||||
if (entry.isDirectory()) {
|
||||
listener.onDirectoryDelete(entry.getFile());
|
||||
} else {
|
||||
listener.onFileDelete(entry.getFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List the contents of a directory
|
||||
*
|
||||
* @param file The file to list the contents of
|
||||
* @return the directory contents or a zero length array if
|
||||
* the empty or the file is not a directory
|
||||
*/
|
||||
private File[] listFiles(final File file) {
|
||||
File[] children = null;
|
||||
if (file.isDirectory()) {
|
||||
children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter);
|
||||
}
|
||||
if (children == null) {
|
||||
children = FileUtils.EMPTY_FILE_ARRAY;
|
||||
}
|
||||
if (comparator != null && children.length > 1) {
|
||||
Arrays.sort(children, comparator);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of this observer.
|
||||
*
|
||||
* @return a String representation of this observer
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append("[file='");
|
||||
builder.append(getDirectory().getPath());
|
||||
builder.append('\'');
|
||||
if (fileFilter != null) {
|
||||
builder.append(", ");
|
||||
builder.append(fileFilter.toString());
|
||||
}
|
||||
builder.append(", listeners=");
|
||||
builder.append(listeners.size());
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* @author Andy Scherzinger
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2016 Tobias Kaminsky, Andy Scherzinger
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* <p>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
* <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 Affero General Public License for more details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.services.observer;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.AdvancedFileAlterationListener;
|
||||
|
||||
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
||||
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public class SyncedFolderObserverService extends Service {
|
||||
private static final String TAG = "SyncedFolderObserverService";
|
||||
|
||||
private static final int MONITOR_SCAN_INTERVAL = 1000;
|
||||
|
||||
private final IBinder mBinder = new SyncedFolderObserverBinder();
|
||||
private FileAlterationMonitor monitor;
|
||||
private FileFilter fileFilter;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(MainApp.getAppContext().
|
||||
getContentResolver());
|
||||
monitor = new FileAlterationMonitor(MONITOR_SCAN_INTERVAL);
|
||||
|
||||
fileFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp") &&
|
||||
!pathname.getName().endsWith(".temp") && !pathname.getName().endsWith(".thumbnail");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if (syncedFolder.isEnabled()) {
|
||||
AdvancedFileAlterationObserver observer = new AdvancedFileAlterationObserver(syncedFolder, fileFilter);
|
||||
|
||||
try {
|
||||
observer.init();
|
||||
observer.addListener(new AdvancedFileAlterationListener(syncedFolder,
|
||||
getResources().getBoolean(R.bool.syncedFolder_light)));
|
||||
monitor.addObserver(observer);
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Failed getting an observer to initialize " + e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
monitor.start();
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Something went very wrong at onStartCommand");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
super.onDestroy();
|
||||
for (FileAlterationObserver fileAlterationObserver : monitor.getObservers()) {
|
||||
AdvancedFileAlterationObserver advancedFileAlterationObserver = (AdvancedFileAlterationObserver)
|
||||
fileAlterationObserver;
|
||||
try {
|
||||
monitor.removeObserver(advancedFileAlterationObserver);
|
||||
advancedFileAlterationObserver.checkAndNotifyNow();
|
||||
advancedFileAlterationObserver.destroy();
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Something went very wrong on trying to destroy observers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart oberver if it is enabled
|
||||
* If syncedFolder exists already, use it, otherwise create new observer
|
||||
*
|
||||
* @param syncedFolder
|
||||
*/
|
||||
|
||||
public void restartObserver(SyncedFolder syncedFolder) {
|
||||
boolean found = false;
|
||||
AdvancedFileAlterationObserver advancedFileAlterationObserver;
|
||||
for (FileAlterationObserver fileAlterationObserver : monitor.getObservers()) {
|
||||
advancedFileAlterationObserver =
|
||||
(AdvancedFileAlterationObserver) fileAlterationObserver;
|
||||
if (advancedFileAlterationObserver.getSyncedFolderID() == syncedFolder.getId()) {
|
||||
monitor.removeObserver(fileAlterationObserver);
|
||||
advancedFileAlterationObserver.checkAndNotifyNow();
|
||||
try {
|
||||
advancedFileAlterationObserver.destroy();
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Failed to destroy the observer in restart");
|
||||
}
|
||||
|
||||
if (syncedFolder.isEnabled()) {
|
||||
try {
|
||||
advancedFileAlterationObserver = new AdvancedFileAlterationObserver(syncedFolder, fileFilter);
|
||||
advancedFileAlterationObserver.init();
|
||||
advancedFileAlterationObserver.addListener(new AdvancedFileAlterationListener(syncedFolder,
|
||||
getResources().getBoolean(R.bool.syncedFolder_light)));
|
||||
monitor.addObserver(advancedFileAlterationObserver);
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Failed getting an observer to initialize");
|
||||
}
|
||||
} else {
|
||||
monitor.removeObserver(fileAlterationObserver);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && syncedFolder.isEnabled()) {
|
||||
try {
|
||||
advancedFileAlterationObserver = new AdvancedFileAlterationObserver(syncedFolder, fileFilter);
|
||||
advancedFileAlterationObserver.init();
|
||||
advancedFileAlterationObserver.addListener(new AdvancedFileAlterationListener(syncedFolder,
|
||||
getResources().getBoolean(R.bool.syncedFolder_light)));
|
||||
monitor.addObserver(advancedFileAlterationObserver);
|
||||
} catch (Exception e) {
|
||||
Log_OC.d(TAG, "Failed getting an observer to initialize");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
public class SyncedFolderObserverBinder extends Binder {
|
||||
public SyncedFolderObserverService getService() {
|
||||
return SyncedFolderObserverService.this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -37,7 +37,7 @@ import com.evernote.android.job.util.support.PersistableBundleCompat;
|
|||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.ContactsBackupJob;
|
||||
import com.owncloud.android.jobs.ContactsBackupJob;
|
||||
import com.owncloud.android.ui.fragment.FileFragment;
|
||||
import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment;
|
||||
import com.owncloud.android.ui.fragment.contactsbackup.ContactsBackupFragment;
|
||||
|
|
|
@ -211,12 +211,6 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
|
|||
setupDrawerMenu(mNavigationView);
|
||||
|
||||
setupQuotaElement();
|
||||
|
||||
// show folder sync menu item only for Android 6+
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M &&
|
||||
mNavigationView.getMenu().findItem(R.id.nav_folder_sync) != null) {
|
||||
mNavigationView.getMenu().removeItem(R.id.nav_folder_sync);
|
||||
}
|
||||
}
|
||||
|
||||
setupDrawerToggle();
|
||||
|
@ -366,7 +360,7 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
|
|||
}
|
||||
|
||||
if (getResources().getBoolean(R.bool.syncedFolder_light)) {
|
||||
navigationView.getMenu().removeItem(R.id.nav_folder_sync);
|
||||
navigationView.getMenu().removeItem(R.id.nav_synced_folders);
|
||||
}
|
||||
|
||||
if (!getResources().getBoolean(R.bool.show_drawer_logout)) {
|
||||
|
@ -450,9 +444,9 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
|
|||
Intent notificationsIntent = new Intent(getApplicationContext(), NotificationsActivity.class);
|
||||
startActivity(notificationsIntent);
|
||||
break;
|
||||
case R.id.nav_folder_sync:
|
||||
Intent folderSyncIntent = new Intent(getApplicationContext(), FolderSyncActivity.class);
|
||||
startActivity(folderSyncIntent);
|
||||
case R.id.nav_synced_folders:
|
||||
Intent syncedFoldersIntent = new Intent(getApplicationContext(), SyncedFoldersActivity.class);
|
||||
startActivity(syncedFoldersIntent);
|
||||
break;
|
||||
case R.id.nav_contacts:
|
||||
Intent contactsIntent = new Intent(getApplicationContext(), ContactsPreferenceActivity.class);
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
|
@ -113,7 +112,6 @@ public class ExternalSiteWebView extends FileActivity {
|
|||
webSettings.setJavaScriptEnabled(true);
|
||||
webSettings.setDomStorageEnabled(true);
|
||||
|
||||
final Activity activity = this;
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
|
||||
webview.setWebChromeClient(new WebChromeClient() {
|
||||
|
@ -124,7 +122,8 @@ public class ExternalSiteWebView extends FileActivity {
|
|||
|
||||
webview.setWebViewClient(new WebViewClient() {
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
webview.loadData(DisplayUtils.getData(getResources().openRawResource(R.raw.custom_error)),"text/html; charset=UTF-8", null);
|
||||
webview.loadData(DisplayUtils.getData(getResources().openRawResource(R.raw.custom_error)),
|
||||
"text/html; charset=UTF-8", null);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -476,7 +476,7 @@ public abstract class FileActivity extends DrawerActivity
|
|||
Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
|
||||
if (frag == null) {
|
||||
Log_OC.d(TAG, "show loading dialog");
|
||||
LoadingDialog loading = new LoadingDialog(message);
|
||||
LoadingDialog loading = LoadingDialog.newInstance(message);
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
FragmentTransaction ft = fm.beginTransaction();
|
||||
loading.show(ft, DIALOG_WAIT_TAG);
|
||||
|
|
|
@ -282,9 +282,8 @@ public class FileDisplayActivity extends HookActivity
|
|||
*/
|
||||
private void upgradeNotificationForInstantUpload() {
|
||||
// check for Android 6+ if legacy instant upload is activated --> disable + show info
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
(PreferenceManager.instantPictureUploadEnabled(this) ||
|
||||
PreferenceManager.instantPictureUploadEnabled(this))) {
|
||||
if (PreferenceManager.instantPictureUploadEnabled(this) ||
|
||||
PreferenceManager.instantPictureUploadEnabled(this)) {
|
||||
|
||||
// remove legacy shared preferences
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
|
||||
|
@ -303,14 +302,14 @@ public class FileDisplayActivity extends HookActivity
|
|||
|
||||
// show info pop-up
|
||||
new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog)
|
||||
.setTitle(R.string.drawer_folder_sync)
|
||||
.setMessage(R.string.folder_sync_new_info)
|
||||
.setTitle(R.string.drawer_synced_folders)
|
||||
.setMessage(R.string.synced_folders_new_info)
|
||||
.setPositiveButton(R.string.drawer_open, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// show instant upload
|
||||
Intent folderSyncIntent = new Intent(getApplicationContext(), FolderSyncActivity.class);
|
||||
Intent syncedFoldersIntent = new Intent(getApplicationContext(), SyncedFoldersActivity.class);
|
||||
dialog.dismiss();
|
||||
startActivity(folderSyncIntent);
|
||||
startActivity(syncedFoldersIntent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.drawer_close, new DialogInterface.OnClickListener() {
|
||||
|
@ -318,7 +317,7 @@ public class FileDisplayActivity extends HookActivity
|
|||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setIcon(R.drawable.nav_folder_sync)
|
||||
.setIcon(R.drawable.nav_synced_folders)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
@ -477,7 +476,7 @@ public class FileDisplayActivity extends HookActivity
|
|||
super.onNewIntent(intent);
|
||||
if(intent.getAction()!=null && intent.getAction().equalsIgnoreCase(ACTION_DETAILS)){
|
||||
setIntent(intent);
|
||||
setFile((OCFile)intent.getParcelableExtra(EXTRA_FILE));
|
||||
setFile(intent.getParcelableExtra(EXTRA_FILE));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -915,7 +914,9 @@ public class FileDisplayActivity extends HookActivity
|
|||
null, // MIME type will be detected from file name
|
||||
behaviour,
|
||||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
} else {
|
||||
|
|
|
@ -77,7 +77,6 @@ public class FingerprintActivity extends AppCompatActivity {
|
|||
|
||||
public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT";
|
||||
|
||||
public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint";
|
||||
public static final String ANDROID_KEY_STORE = "AndroidKeyStore";
|
||||
|
||||
private KeyStore keyStore;
|
||||
|
@ -286,7 +285,7 @@ public class FingerprintActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
|
||||
|
||||
private TextView text;
|
||||
|
|
|
@ -302,6 +302,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
|
|||
retval = super.onOptionsItemSelected(item);
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -273,9 +273,7 @@ public class LogHistoryActivity extends ToolbarActivity {
|
|||
*/
|
||||
public void showLoadingDialog() {
|
||||
// Construct dialog
|
||||
LoadingDialog loading = new LoadingDialog(
|
||||
getResources().getString(R.string.log_progress_dialog_text)
|
||||
);
|
||||
LoadingDialog loading = LoadingDialog.newInstance(getResources().getString(R.string.log_progress_dialog_text));
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
FragmentTransaction ft = fm.beginTransaction();
|
||||
loading.show(ft, DIALOG_WAIT_TAG);
|
||||
|
|
|
@ -50,8 +50,7 @@ import com.owncloud.android.files.services.FileDownloader;
|
|||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.AccountRemovalJob;
|
||||
import com.owncloud.android.services.AutoUploadJob;
|
||||
import com.owncloud.android.jobs.AccountRemovalJob;
|
||||
import com.owncloud.android.services.OperationsService;
|
||||
import com.owncloud.android.ui.adapter.AccountListAdapter;
|
||||
import com.owncloud.android.ui.adapter.AccountListItem;
|
||||
|
@ -407,7 +406,7 @@ public class ManageAccountsActivity extends FileActivity
|
|||
|
||||
// store pending account removal
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account, PENDING_FOR_REMOVAL, String.valueOf(true));
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PENDING_FOR_REMOVAL, String.valueOf(true));
|
||||
|
||||
// Cancel transfers
|
||||
if (mUploaderBinder != null) {
|
||||
|
@ -419,7 +418,7 @@ public class ManageAccountsActivity extends FileActivity
|
|||
|
||||
// schedule job
|
||||
PersistableBundleCompat bundle = new PersistableBundleCompat();
|
||||
bundle.putString(AutoUploadJob.ACCOUNT, account.name);
|
||||
bundle.putString(AccountRemovalJob.ACCOUNT, account.name);
|
||||
|
||||
new JobRequest.Builder(AccountRemovalJob.TAG)
|
||||
.setExecutionWindow(1_000L, 10_000L)
|
||||
|
|
|
@ -85,9 +85,11 @@ import java.io.IOException;
|
|||
*/
|
||||
public class Preferences extends PreferenceActivity
|
||||
implements StorageMigration.StorageMigrationProgressListener {
|
||||
|
||||
|
||||
private static final String TAG = Preferences.class.getSimpleName();
|
||||
|
||||
public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint";
|
||||
|
||||
private static final String SCREEN_NAME = "Settings";
|
||||
|
||||
private static final int ACTION_SELECT_UPLOAD_PATH = 1;
|
||||
|
@ -107,6 +109,7 @@ public class Preferences extends PreferenceActivity
|
|||
private SwitchPreference pCode;
|
||||
private SwitchPreference fPrint;
|
||||
private SwitchPreference mShowHiddenFiles;
|
||||
private SwitchPreference mExpertMode;
|
||||
private Preference pAboutApp;
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
|
@ -190,13 +193,14 @@ public class Preferences extends PreferenceActivity
|
|||
accentColor));
|
||||
|
||||
// Synced folders
|
||||
PreferenceCategory preferenceCategoryFolderSync = (PreferenceCategory) findPreference("folder_sync");
|
||||
preferenceCategoryFolderSync.setTitle(ThemeUtils.getColoredTitle(getString(R.string.drawer_folder_sync),
|
||||
PreferenceCategory preferenceCategorySyncedFolders =
|
||||
(PreferenceCategory) findPreference("synced_folders_category");
|
||||
preferenceCategorySyncedFolders.setTitle(ThemeUtils.getColoredTitle(getString(R.string.drawer_synced_folders),
|
||||
accentColor));
|
||||
PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference("preference_screen");
|
||||
|
||||
if (!getResources().getBoolean(R.bool.syncedFolder_light)) {
|
||||
preferenceScreen.removePreference(preferenceCategoryFolderSync);
|
||||
preferenceScreen.removePreference(preferenceCategorySyncedFolders);
|
||||
} else {
|
||||
// Upload on WiFi
|
||||
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
|
||||
|
@ -209,28 +213,28 @@ public class Preferences extends PreferenceActivity
|
|||
pUploadOnWifiCheckbox.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI,
|
||||
String.valueOf(pUploadOnWifiCheckbox.isChecked()));
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Preference pSyncedFolder = findPreference("folder_sync_folders");
|
||||
Preference pSyncedFolder = findPreference("synced_folders_configure_folders");
|
||||
if (pSyncedFolder != null) {
|
||||
if (getResources().getBoolean(R.bool.syncedFolder_light)
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
pSyncedFolder.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent folderSyncIntent = new Intent(getApplicationContext(), FolderSyncActivity.class);
|
||||
folderSyncIntent.putExtra(FolderSyncActivity.EXTRA_SHOW_SIDEBAR, false);
|
||||
startActivity(folderSyncIntent);
|
||||
Intent syncedFoldersIntent = new Intent(getApplicationContext(), SyncedFoldersActivity.class);
|
||||
syncedFoldersIntent.putExtra(SyncedFoldersActivity.EXTRA_SHOW_SIDEBAR, false);
|
||||
startActivity(syncedFoldersIntent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
preferenceCategoryFolderSync.removePreference(pSyncedFolder);
|
||||
preferenceCategorySyncedFolders.removePreference(pSyncedFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +269,7 @@ public class Preferences extends PreferenceActivity
|
|||
}
|
||||
|
||||
boolean fPrintEnabled = getResources().getBoolean(R.bool.fingerprint_enabled);
|
||||
fPrint = (SwitchPreference) findPreference(FingerprintActivity.PREFERENCE_USE_FINGERPRINT);
|
||||
fPrint = (SwitchPreference) findPreference(PREFERENCE_USE_FINGERPRINT);
|
||||
if (fPrint != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (FingerprintActivity.isFingerprintCapable(MainApp.getAppContext()) && fPrintEnabled) {
|
||||
|
@ -332,9 +336,21 @@ public class Preferences extends PreferenceActivity
|
|||
});
|
||||
} else {
|
||||
preferenceCategoryDetails.removePreference(mShowHiddenFiles);
|
||||
|
||||
}
|
||||
|
||||
mExpertMode = (SwitchPreference) findPreference("expert_mode");
|
||||
mExpertMode.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences.Editor editor = appPrefs.edit();
|
||||
editor.putBoolean("expert_mode", mExpertMode.isChecked());
|
||||
editor.apply();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
PreferenceCategory preferenceCategoryMore = (PreferenceCategory) findPreference("more");
|
||||
preferenceCategoryMore.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_more),
|
||||
accentColor));
|
||||
|
@ -468,7 +484,11 @@ public class Preferences extends PreferenceActivity
|
|||
}
|
||||
}
|
||||
|
||||
boolean loggerEnabled = getResources().getBoolean(R.bool.logger_enabled) || BuildConfig.DEBUG;
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
|
||||
boolean loggerEnabled = getResources().getBoolean(R.bool.logger_enabled) || BuildConfig.DEBUG ||
|
||||
appPrefs.getBoolean("expert_mode", false);
|
||||
Preference pLogger = findPreference("logger");
|
||||
if (pLogger != null) {
|
||||
if (loggerEnabled) {
|
||||
|
@ -521,12 +541,12 @@ public class Preferences extends PreferenceActivity
|
|||
mPrefStoragePath.setEntryValues(values);
|
||||
|
||||
mPrefStoragePath.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String newPath = (String) newValue;
|
||||
if (mStoragePath.equals(newPath)) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String newPath = (String) newValue;
|
||||
if (mStoragePath.equals(newPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
StorageMigration storageMigration = new StorageMigration(Preferences.this, mStoragePath, newPath);
|
||||
|
||||
|
@ -540,89 +560,7 @@ public class Preferences extends PreferenceActivity
|
|||
}
|
||||
|
||||
mPrefInstantUploadCategory = (PreferenceCategory) findPreference("instant_uploading_category");
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// Instant upload via preferences on pre Android Marshmallow
|
||||
mPrefInstantUploadPath = findPreference("instant_upload_path");
|
||||
if (mPrefInstantUploadPath != null) {
|
||||
|
||||
mPrefInstantUploadPath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (!mUploadPath.endsWith(OCFile.PATH_SEPARATOR)) {
|
||||
mUploadPath += OCFile.PATH_SEPARATOR;
|
||||
}
|
||||
Intent intent = new Intent(Preferences.this, UploadPathActivity.class);
|
||||
intent.putExtra(UploadPathActivity.KEY_INSTANT_UPLOAD_PATH, mUploadPath);
|
||||
startActivityForResult(intent, ACTION_SELECT_UPLOAD_PATH);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mPrefInstantUploadCategory = (PreferenceCategory) findPreference("instant_uploading_category");
|
||||
|
||||
mPrefInstantUploadUseSubfolders = findPreference("instant_upload_path_use_subfolders");
|
||||
mPrefInstantUploadPathWiFi = findPreference("instant_upload_on_wifi");
|
||||
mPrefInstantPictureUploadOnlyOnCharging = findPreference("instant_upload_on_charging");
|
||||
mPrefInstantUpload = (CheckBoxPreferenceWithLongTitle) findPreference("instant_uploading");
|
||||
|
||||
toggleInstantPictureOptions(mPrefInstantUpload.isChecked());
|
||||
|
||||
mPrefInstantUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
toggleInstantPictureOptions((Boolean) newValue);
|
||||
toggleInstantUploadBehaviour(mPrefInstantVideoUpload.isChecked(), (Boolean) newValue);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mPrefInstantVideoUploadPath = findPreference(PreferenceKeys.INSTANT_VIDEO_UPLOAD_PATH);
|
||||
if (mPrefInstantVideoUploadPath != null) {
|
||||
|
||||
mPrefInstantVideoUploadPath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (!mUploadVideoPath.endsWith(OCFile.PATH_SEPARATOR)) {
|
||||
mUploadVideoPath += OCFile.PATH_SEPARATOR;
|
||||
}
|
||||
Intent intent = new Intent(Preferences.this, UploadPathActivity.class);
|
||||
intent.putExtra(UploadPathActivity.KEY_INSTANT_UPLOAD_PATH,
|
||||
mUploadVideoPath);
|
||||
startActivityForResult(intent, ACTION_SELECT_UPLOAD_VIDEO_PATH);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mPrefInstantVideoUploadUseSubfolders = findPreference("instant_video_upload_path_use_subfolders");
|
||||
mPrefInstantVideoUploadPathWiFi = findPreference("instant_video_upload_on_wifi");
|
||||
mPrefInstantVideoUpload = (CheckBoxPreferenceWithLongTitle) findPreference("instant_video_uploading");
|
||||
mPrefInstantVideoUploadOnlyOnCharging = findPreference("instant_video_upload_on_charging");
|
||||
toggleInstantVideoOptions(mPrefInstantVideoUpload.isChecked());
|
||||
mPrefInstantVideoUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
toggleInstantVideoOptions((Boolean) newValue);
|
||||
toggleInstantUploadBehaviour(
|
||||
(Boolean) newValue,
|
||||
mPrefInstantUpload.isChecked());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mPrefInstantUploadBehaviour = findPreference("prefs_instant_behaviour");
|
||||
toggleInstantUploadBehaviour(
|
||||
mPrefInstantVideoUpload.isChecked(),
|
||||
mPrefInstantUpload.isChecked());
|
||||
|
||||
loadInstantUploadPath();
|
||||
loadInstantUploadVideoPath();
|
||||
} else {
|
||||
// Instant upload is handled via synced folders on Android Lollipop and up
|
||||
getPreferenceScreen().removePreference(mPrefInstantUploadCategory);
|
||||
}
|
||||
getPreferenceScreen().removePreference(mPrefInstantUploadCategory);
|
||||
|
||||
// About category
|
||||
PreferenceCategory preferenceCategoryAbout = (PreferenceCategory) findPreference("about");
|
||||
|
@ -716,48 +654,12 @@ public class Preferences extends PreferenceActivity
|
|||
mUri = OwnCloudClientManagerFactory.getDefaultSingleton().
|
||||
getClientFor(ocAccount, getApplicationContext()).getBaseUri();
|
||||
} catch (Throwable t) {
|
||||
Log_OC.e(TAG,"Error retrieving user's base URI", t);
|
||||
Log_OC.e(TAG, "Error retrieving user's base URI", t);
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void toggleInstantPictureOptions(Boolean value){
|
||||
if (value) {
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPathWiFi);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPath);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantUploadUseSubfolders);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantPictureUploadOnlyOnCharging);
|
||||
} else {
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantUploadUseSubfolders);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantPictureUploadOnlyOnCharging);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleInstantVideoOptions(Boolean value){
|
||||
if (value) {
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPathWiFi);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPath);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadUseSubfolders);
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadOnlyOnCharging);
|
||||
} else {
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadUseSubfolders);
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadOnlyOnCharging);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleInstantUploadBehaviour(Boolean video, Boolean picture){
|
||||
if (picture || video) {
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantUploadBehaviour);
|
||||
} else {
|
||||
mPrefInstantUploadCategory.removePreference(mPrefInstantUploadBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
|
@ -782,14 +684,14 @@ public class Preferences extends PreferenceActivity
|
|||
Intent intent;
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
intent = new Intent(getBaseContext(), FileDisplayActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
break;
|
||||
default:
|
||||
Log_OC.w(TAG, "Unknown menu item triggered");
|
||||
return false;
|
||||
case android.R.id.home:
|
||||
intent = new Intent(getBaseContext(), FileDisplayActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
break;
|
||||
default:
|
||||
Log_OC.w(TAG, "Unknown menu item triggered");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -800,7 +702,7 @@ public class Preferences extends PreferenceActivity
|
|||
|
||||
if (requestCode == ACTION_SELECT_UPLOAD_PATH && resultCode == RESULT_OK) {
|
||||
|
||||
OCFile folderToUpload = data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
OCFile folderToUpload = data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
|
||||
mUploadPath = folderToUpload.getRemotePath();
|
||||
|
||||
|
@ -830,7 +732,7 @@ public class Preferences extends PreferenceActivity
|
|||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||
|
||||
for (int i = 1; i <= 4; ++i) {
|
||||
appPrefs.putString(PassCodeActivity.PREFERENCE_PASSCODE_D + i, passcode.substring(i-1, i));
|
||||
appPrefs.putString(PassCodeActivity.PREFERENCE_PASSCODE_D + i, passcode.substring(i - 1, i));
|
||||
}
|
||||
appPrefs.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, true);
|
||||
appPrefs.apply();
|
||||
|
@ -868,10 +770,12 @@ public class Preferences extends PreferenceActivity
|
|||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
|
@ -994,7 +898,7 @@ public class Preferences extends PreferenceActivity
|
|||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
mStoragePath = appPrefs.getString(PreferenceKeys.STORAGE_PATH, Environment.getExternalStorageDirectory()
|
||||
.getAbsolutePath());
|
||||
.getAbsolutePath());
|
||||
String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
|
||||
mPrefStoragePath.setSummary(storageDescription);
|
||||
}
|
||||
|
|
|
@ -913,7 +913,9 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
|||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
null,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -55,11 +55,9 @@ import com.owncloud.android.utils.GetShareWithUsersAsyncTask;
|
|||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Activity for sharing files
|
||||
* Activity for sharing files.
|
||||
*/
|
||||
|
||||
public class ShareActivity extends FileActivity
|
||||
implements ShareFragmentListener {
|
||||
public class ShareActivity extends FileActivity implements ShareFragmentListener {
|
||||
|
||||
private static final String TAG = ShareActivity.class.getSimpleName();
|
||||
|
||||
|
@ -70,8 +68,6 @@ public class ShareActivity extends FileActivity
|
|||
|
||||
/// Tags for dialog fragments
|
||||
private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
|
||||
private static final String FTAG_SHARE_PASSWORD_DIALOG = "SHARE_PASSWORD_DIALOG";
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -87,7 +83,6 @@ public class ShareActivity extends FileActivity
|
|||
ft.replace(R.id.share_fragment_container, fragment, TAG_SHARE_FRAGMENT);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void onAccountSet(boolean stateWasRecovered) {
|
||||
|
@ -144,10 +139,9 @@ public class ShareActivity extends FileActivity
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
private int getAppropiatePermissions(ShareType shareType) {
|
||||
|
||||
// check if the Share is FERERATED
|
||||
// check if the Share is FEDERATED
|
||||
boolean isFederated = ShareType.FEDERATED.equals(shareType);
|
||||
|
||||
if (getFile().isSharedWithMe()) {
|
||||
|
@ -245,9 +239,8 @@ public class ShareActivity extends FileActivity
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager}
|
||||
* Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager}.
|
||||
*/
|
||||
private void refreshSharesFromStorageManager() {
|
||||
|
||||
|
@ -270,7 +263,6 @@ public class ShareActivity extends FileActivity
|
|||
editShareFragment.isAdded()) {
|
||||
editShareFragment.refreshUiFromDB();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,7 +292,6 @@ public class ShareActivity extends FileActivity
|
|||
return (EditShareFragment) getSupportFragmentManager().findFragmentByTag(TAG_EDIT_SHARE_FRAGMENT);
|
||||
}
|
||||
|
||||
|
||||
private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
|
||||
RemoteOperationResult result) {
|
||||
if (result.isSuccess()) {
|
||||
|
@ -369,8 +360,5 @@ public class ShareActivity extends FileActivity
|
|||
t.show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,56 +23,66 @@ package com.owncloud.android.ui.activity;
|
|||
|
||||
import android.accounts.Account;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.AppBarLayout;
|
||||
import android.support.design.widget.BottomNavigationView;
|
||||
import android.support.design.widget.CollapsingToolbarLayout;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.MediaFolder;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.MediaProvider;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.ui.adapter.FolderSyncAdapter;
|
||||
import com.owncloud.android.ui.adapter.SyncedFolderAdapter;
|
||||
import com.owncloud.android.ui.decoration.MediaGridItemDecoration;
|
||||
import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
|
||||
import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable;
|
||||
import com.owncloud.android.utils.AnalyticsUtils;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.PermissionUtil;
|
||||
import com.owncloud.android.utils.ThemeUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static android.support.design.widget.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS;
|
||||
import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID;
|
||||
|
||||
/**
|
||||
* Activity displaying all auto-synced folders and/or instant upload media folders.
|
||||
*/
|
||||
public class FolderSyncActivity extends FileActivity implements FolderSyncAdapter.ClickListener,
|
||||
public class SyncedFoldersActivity extends FileActivity implements SyncedFolderAdapter.ClickListener,
|
||||
SyncedFolderPreferencesDialogFragment.OnSyncedFolderPreferenceListener {
|
||||
|
||||
private static final String SYNCED_FOLDER_PREFERENCES_DIALOG_TAG = "SYNCED_FOLDER_PREFERENCES_DIALOG";
|
||||
|
@ -81,16 +91,16 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
|
||||
private static final String SCREEN_NAME = "Auto upload";
|
||||
|
||||
private static final String TAG = FolderSyncActivity.class.getSimpleName();
|
||||
private static final String TAG = SyncedFoldersActivity.class.getSimpleName();
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private FolderSyncAdapter mAdapter;
|
||||
private SyncedFolderAdapter mAdapter;
|
||||
private LinearLayout mProgress;
|
||||
private TextView mEmpty;
|
||||
private SyncedFolderProvider mSyncedFolderProvider;
|
||||
private List<SyncedFolderDisplayItem> syncFolderItems;
|
||||
private SyncedFolderPreferencesDialogFragment mSyncedFolderPreferencesDialogFragment;
|
||||
private boolean showSidebar = true;
|
||||
private RelativeLayout mCustomFolderRelativeLayout;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -100,13 +110,37 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
showSidebar = getIntent().getExtras().getBoolean(EXTRA_SHOW_SIDEBAR);
|
||||
}
|
||||
|
||||
setContentView(R.layout.folder_sync_layout);
|
||||
setContentView(R.layout.synced_folders_layout);
|
||||
|
||||
// setup toolbar
|
||||
setupToolbar();
|
||||
CollapsingToolbarLayout mCollapsingToolbarLayout = ((CollapsingToolbarLayout)
|
||||
findViewById(R.id.collapsing_toolbar));
|
||||
mCollapsingToolbarLayout.setTitle(this.getString(R.string.drawer_synced_folders));
|
||||
|
||||
mCustomFolderRelativeLayout = (RelativeLayout) findViewById(R.id.custom_folder_toolbar);
|
||||
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
|
||||
|
||||
findViewById(R.id.toolbar).post(() -> {
|
||||
if (!appPrefs.getBoolean("expert_mode", false)) {
|
||||
findViewById(R.id.app_bar).getLayoutParams().height = findViewById(R.id.toolbar).getHeight();
|
||||
|
||||
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) mCollapsingToolbarLayout.getLayoutParams();
|
||||
p.setScrollFlags(SCROLL_FLAG_ENTER_ALWAYS);
|
||||
mCollapsingToolbarLayout.setLayoutParams(p);
|
||||
mCustomFolderRelativeLayout.setVisibility(View.GONE);
|
||||
} else {
|
||||
mCustomFolderRelativeLayout.setVisibility(View.VISIBLE);
|
||||
findViewById(R.id.app_bar).setBackgroundColor(getResources().getColor(R.color.filelist_icon_backgorund));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// setup drawer
|
||||
setupDrawer(R.id.nav_folder_sync);
|
||||
setupDrawer(R.id.nav_synced_folders);
|
||||
|
||||
if (!showSidebar) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
|
@ -117,7 +151,7 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
ThemeUtils.setColoredTitle(getSupportActionBar(), getString(R.string.drawer_folder_sync));
|
||||
ThemeUtils.setColoredTitle(getSupportActionBar(), getString(R.string.drawer_synced_folders));
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
|
@ -143,7 +177,7 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
|
||||
final int gridWidth = getResources().getInteger(R.integer.media_grid_width);
|
||||
boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light);
|
||||
mAdapter = new FolderSyncAdapter(this, gridWidth, this, lightVersion);
|
||||
mAdapter = new SyncedFolderAdapter(this, gridWidth, this, lightVersion);
|
||||
mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver());
|
||||
|
||||
final GridLayoutManager lm = new GridLayoutManager(this, gridWidth);
|
||||
|
@ -173,33 +207,25 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
return;
|
||||
}
|
||||
setListShown(false);
|
||||
final Handler mHandler = new Handler();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final List<MediaFolder> mediaFolders = MediaProvider.getMediaFolders(getContentResolver(),
|
||||
perFolderMediaItemLimit, FolderSyncActivity.this);
|
||||
List<SyncedFolder> syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders();
|
||||
List<SyncedFolder> currentAccountSyncedFoldersList = new ArrayList<>();
|
||||
Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(FolderSyncActivity.this);
|
||||
for (SyncedFolder syncedFolder : syncedFolderArrayList) {
|
||||
if (syncedFolder.getAccount().equals(currentAccount.name)) {
|
||||
currentAccountSyncedFoldersList.add(syncedFolder);
|
||||
}
|
||||
}
|
||||
final List<MediaFolder> mediaFolders = MediaProvider.getImageFolders(getContentResolver(),
|
||||
perFolderMediaItemLimit, SyncedFoldersActivity.this);
|
||||
mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit));
|
||||
|
||||
syncFolderItems = sortSyncedFolderItems(mergeFolderData(currentAccountSyncedFoldersList,
|
||||
mediaFolders));
|
||||
|
||||
mHandler.post(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAdapter.setSyncFolderItems(syncFolderItems);
|
||||
setListShown(true);
|
||||
}
|
||||
});
|
||||
List<SyncedFolder> syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders();
|
||||
List<SyncedFolder> currentAccountSyncedFoldersList = new ArrayList<>();
|
||||
Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(SyncedFoldersActivity.this);
|
||||
for (SyncedFolder syncedFolder : syncedFolderArrayList) {
|
||||
if (syncedFolder.getAccount().equals(currentAccount.name)) {
|
||||
currentAccountSyncedFoldersList.add(syncedFolder);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
List<SyncedFolderDisplayItem> syncFolderItems = sortSyncedFolderItems(
|
||||
mergeFolderData(currentAccountSyncedFoldersList, mediaFolders));
|
||||
|
||||
mAdapter.setSyncFolderItems(syncFolderItems);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
setListShown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,20 +241,23 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
Map<String, SyncedFolder> syncedFoldersMap = createSyncedFoldersMap(syncedFolders);
|
||||
List<SyncedFolderDisplayItem> result = new ArrayList<>();
|
||||
|
||||
|
||||
for (MediaFolder mediaFolder : mediaFolders) {
|
||||
if (syncedFoldersMap.containsKey(mediaFolder.absolutePath)) {
|
||||
SyncedFolder syncedFolder = syncedFoldersMap.get(mediaFolder.absolutePath);
|
||||
syncedFoldersMap.remove(mediaFolder.absolutePath);
|
||||
result.add(createSyncedFolder(syncedFolder, mediaFolder));
|
||||
if (syncedFoldersMap.containsKey(mediaFolder.absolutePath+"-"+mediaFolder.type)) {
|
||||
SyncedFolder syncedFolder = syncedFoldersMap.get(mediaFolder.absolutePath+"-"+mediaFolder.type);
|
||||
syncedFoldersMap.remove(mediaFolder.absolutePath+"-"+mediaFolder.type);
|
||||
|
||||
if (MediaFolderType.CUSTOM == syncedFolder.getType()) {
|
||||
result.add(createSyncedFolderWithoutMediaFolder(syncedFolder));
|
||||
} else {
|
||||
result.add(createSyncedFolder(syncedFolder, mediaFolder));
|
||||
}
|
||||
} else {
|
||||
result.add(createSyncedFolderFromMediaFolder(mediaFolder));
|
||||
}
|
||||
}
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFoldersMap.values()) {
|
||||
SyncedFolderDisplayItem syncedFolderDisplayItem = createSyncedFolderWithoutMediaFolder(syncedFolder);
|
||||
result.add(syncedFolderDisplayItem);
|
||||
result.add(createSyncedFolderWithoutMediaFolder(syncedFolder));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -277,6 +306,11 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
|
||||
@NonNull
|
||||
private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull SyncedFolder syncedFolder) {
|
||||
|
||||
File localFolder = new File(syncedFolder.getLocalPath());
|
||||
File[] files = getFileList(localFolder);
|
||||
List<String> filePaths = getDisplayFilePathList(files);
|
||||
|
||||
return new SyncedFolderDisplayItem(
|
||||
syncedFolder.getId(),
|
||||
syncedFolder.getLocalPath(),
|
||||
|
@ -287,7 +321,10 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
syncedFolder.getAccount(),
|
||||
syncedFolder.getUploadAction(),
|
||||
syncedFolder.isEnabled(),
|
||||
new File(syncedFolder.getLocalPath()).getName());
|
||||
filePaths,
|
||||
localFolder.getName(),
|
||||
files.length,
|
||||
syncedFolder.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,7 +348,8 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
syncedFolder.isEnabled(),
|
||||
mediaFolder.filePaths,
|
||||
mediaFolder.folderName,
|
||||
mediaFolder.numberOfFiles);
|
||||
mediaFolder.numberOfFiles,
|
||||
mediaFolder.type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -334,7 +372,42 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
false,
|
||||
mediaFolder.filePaths,
|
||||
mediaFolder.folderName,
|
||||
mediaFolder.numberOfFiles);
|
||||
mediaFolder.numberOfFiles,
|
||||
mediaFolder.type);
|
||||
}
|
||||
|
||||
private File[] getFileList(File localFolder) {
|
||||
File[] files = localFolder.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return !pathname.isDirectory();
|
||||
}
|
||||
});
|
||||
|
||||
if (files != null) {
|
||||
Arrays.sort(files, new Comparator<File>() {
|
||||
public int compare(File f1, File f2) {
|
||||
return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
files = new File[]{};
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private List<String> getDisplayFilePathList(File[] files) {
|
||||
List<String> filePaths = null;
|
||||
|
||||
if (files != null && files.length > 0) {
|
||||
filePaths = new ArrayList<>();
|
||||
for (int i = 0; i < 7 && i < files.length; i++) {
|
||||
filePaths.add(files[i].getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -348,7 +421,7 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
Map<String, SyncedFolder> result = new HashMap<>();
|
||||
if (syncFolders != null) {
|
||||
for (SyncedFolder syncFolder : syncFolders) {
|
||||
result.put(syncFolder.getLocalPath(), syncFolder);
|
||||
result.put(syncFolder.getLocalPath()+"-"+syncFolder.getType(), syncFolder);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -389,6 +462,7 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
result = super.onOptionsItemSelected(item);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -409,15 +483,27 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
|
||||
@Override
|
||||
public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) {
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().
|
||||
getContentResolver());
|
||||
|
||||
if (syncedFolderDisplayItem.getId() > UNPERSISTED_ID) {
|
||||
mSyncedFolderProvider.updateFolderSyncEnabled(syncedFolderDisplayItem.getId(),
|
||||
mSyncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(),
|
||||
syncedFolderDisplayItem.isEnabled());
|
||||
} else {
|
||||
long storedId = mSyncedFolderProvider.storeFolderSync(syncedFolderDisplayItem);
|
||||
long storedId = mSyncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem);
|
||||
if (storedId != -1) {
|
||||
syncedFolderDisplayItem.setId(storedId);
|
||||
}
|
||||
}
|
||||
|
||||
if (syncedFolderDisplayItem.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + syncedFolderDisplayItem.getId();
|
||||
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
|
||||
}
|
||||
FilesSyncHelper.scheduleNJobs(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -437,36 +523,76 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
&& resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) {
|
||||
OCFile chosenFolder = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
|
||||
mSyncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath());
|
||||
|
||||
} else {
|
||||
} if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_LOCAL_FOLDER
|
||||
&& resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) {
|
||||
String localPath = data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
|
||||
mSyncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath);
|
||||
}
|
||||
else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) {
|
||||
SyncedFolderDisplayItem item = syncFolderItems.get(syncedFolder.getSection());
|
||||
boolean dirty = item.isEnabled() != syncedFolder.getEnabled();
|
||||
item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder
|
||||
.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder
|
||||
.getUploadAction(), syncedFolder.getEnabled());
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().
|
||||
getContentResolver());
|
||||
|
||||
if (syncedFolder.getId() == UNPERSISTED_ID) {
|
||||
// newly set up folder sync config
|
||||
long storedId = mSyncedFolderProvider.storeFolderSync(item);
|
||||
// custom folders newly created aren't in the list already,
|
||||
// so triggering a refresh
|
||||
if (MediaFolderType.CUSTOM.equals(syncedFolder.getType()) && syncedFolder.getId() == UNPERSISTED_ID) {
|
||||
SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem(
|
||||
SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(),
|
||||
syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(),
|
||||
syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(),
|
||||
new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType());
|
||||
long storedId = mSyncedFolderProvider.storeSyncedFolder(newCustomFolder);
|
||||
if (storedId != -1) {
|
||||
item.setId(storedId);
|
||||
newCustomFolder.setId(storedId);
|
||||
if (newCustomFolder.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(newCustomFolder);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + newCustomFolder.getId();
|
||||
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
|
||||
}
|
||||
FilesSyncHelper.scheduleNJobs(false);
|
||||
}
|
||||
mAdapter.addSyncFolderItem(newCustomFolder);
|
||||
} else {
|
||||
SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection());
|
||||
item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder
|
||||
.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder
|
||||
.getUploadAction(), syncedFolder.getEnabled());
|
||||
|
||||
if (syncedFolder.getId() == UNPERSISTED_ID) {
|
||||
// newly set up folder sync config
|
||||
long storedId = mSyncedFolderProvider.storeSyncedFolder(item);
|
||||
if (storedId != -1) {
|
||||
item.setId(storedId);
|
||||
if (item.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
|
||||
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
|
||||
}
|
||||
FilesSyncHelper.scheduleNJobs(false);
|
||||
}
|
||||
} else {
|
||||
// existing synced folder setup to be updated
|
||||
mSyncedFolderProvider.updateSyncFolder(item);
|
||||
if (item.isEnabled()) {
|
||||
FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
|
||||
} else {
|
||||
String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
|
||||
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
|
||||
}
|
||||
FilesSyncHelper.scheduleNJobs(false);
|
||||
}
|
||||
|
||||
} else {
|
||||
// existing synced folder setup to be updated
|
||||
mSyncedFolderProvider.updateSyncFolder(item);
|
||||
}
|
||||
mSyncedFolderPreferencesDialogFragment = null;
|
||||
|
||||
if (dirty) {
|
||||
mAdapter.setSyncFolderItem(syncedFolder.getSection(), item);
|
||||
}
|
||||
|
||||
mSyncedFolderPreferencesDialogFragment = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -474,6 +600,12 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
mSyncedFolderPreferencesDialogFragment = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) {
|
||||
mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId());
|
||||
mAdapter.removeItem(syncedFolder.getSection());
|
||||
}
|
||||
|
||||
/**
|
||||
* update given synced folder with the given values.
|
||||
*
|
||||
|
@ -525,4 +657,13 @@ public class FolderSyncActivity extends FileActivity implements FolderSyncAdapte
|
|||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
public void onAddCustomFolderClick(View view) {
|
||||
Log.d(TAG, "Show custom folder dialog");
|
||||
SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem(
|
||||
SyncedFolder.UNPERSISTED_ID, null, null, true, false,
|
||||
false, AccountUtils.getCurrentOwnCloudAccount(this).name,
|
||||
FileUploader.LOCAL_BEHAVIOUR_FORGET, false, null, MediaFolderType.CUSTOM);
|
||||
onSyncFolderSettingsClick(0, emptyCustomFolder);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@ import android.graphics.PorterDuff;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
@ -75,6 +77,7 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
private ArrayAdapter<String> mDirectories;
|
||||
private File mCurrentDir = null;
|
||||
private boolean mSelectAll = false;
|
||||
private boolean mLocalFolderPickerMode = false;
|
||||
private LocalFileListFragment mFileListFragment;
|
||||
private Button mCancelBtn;
|
||||
protected Button mUploadBtn;
|
||||
|
@ -88,6 +91,10 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
public static final String EXTRA_CHOSEN_FILES =
|
||||
UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES";
|
||||
|
||||
public static final String EXTRA_ACTION = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_ACTION";
|
||||
public final static String KEY_LOCAL_FOLDER_PICKER_MODE = UploadFilesActivity.class.getCanonicalName()
|
||||
+ ".LOCAL_FOLDER_PICKER_MODE";
|
||||
|
||||
public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER;
|
||||
public static final int RESULT_OK_AND_DO_NOTHING = 2;
|
||||
public static final int RESULT_OK_AND_DELETE = 3;
|
||||
|
@ -106,6 +113,11 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
Log_OC.d(TAG, "onCreate() start");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
mLocalFolderPickerMode = extras.getBoolean(KEY_LOCAL_FOLDER_PICKER_MODE, false);
|
||||
}
|
||||
|
||||
if(savedInstanceState != null) {
|
||||
mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH, Environment
|
||||
.getExternalStorageDirectory().getAbsolutePath()));
|
||||
|
@ -130,9 +142,14 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
// Inflate and set the layout view
|
||||
setContentView(R.layout.upload_files_layout);
|
||||
|
||||
if (mLocalFolderPickerMode) {
|
||||
findViewById(R.id.upload_options).setVisibility(View.GONE);
|
||||
((AppCompatButton) findViewById(R.id.upload_files_btn_upload))
|
||||
.setText(R.string.uploader_btn_alternative_text);
|
||||
}
|
||||
|
||||
mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list);
|
||||
|
||||
|
||||
// Set input controllers
|
||||
mCancelBtn = (Button) findViewById(R.id.upload_files_btn_cancel);
|
||||
mCancelBtn.setOnClickListener(this);
|
||||
|
@ -190,10 +207,15 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
mOptionsMenu = menu;
|
||||
getMenuInflater().inflate(R.menu.upload_files_picker, menu);
|
||||
MenuItem selectAll = menu.findItem(R.id.action_select_all);
|
||||
setSelectAllMenuItem(selectAll, mSelectAll);
|
||||
|
||||
if(!mLocalFolderPickerMode) {
|
||||
MenuItem selectAll = menu.findItem(R.id.action_select_all);
|
||||
setSelectAllMenuItem(selectAll, mSelectAll);
|
||||
}
|
||||
|
||||
MenuItem switchView = menu.findItem(R.id.action_switch_view);
|
||||
switchView.setTitle(isGridView() ? R.string.action_switch_list_view : R.string.action_switch_grid_view);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
@ -215,9 +237,6 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
break;
|
||||
}
|
||||
case R.id.action_sort: {
|
||||
// Read sorting order, default to sort by name ascending
|
||||
Integer sortOrder = PreferenceManager.getSortOrder(this);
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
FragmentTransaction ft = fm.beginTransaction();
|
||||
ft.addToBackStack(null);
|
||||
|
@ -290,7 +309,6 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
|
@ -308,9 +326,10 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
}
|
||||
|
||||
// invalidate checked state when navigating directories
|
||||
setSelectAllMenuItem(mOptionsMenu.findItem(R.id.action_select_all), false);
|
||||
if(!mLocalFolderPickerMode) {
|
||||
setSelectAllMenuItem(mOptionsMenu.findItem(R.id.action_select_all), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
|
@ -361,15 +380,16 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Custom array adapter to override text colors
|
||||
/**
|
||||
* Custom array adapter to override text colors
|
||||
*/
|
||||
private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
|
||||
|
||||
public CustomArrayAdapter(UploadFilesActivity ctx, int view) {
|
||||
super(ctx, view);
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
public @NonNull View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View v = super.getView(position, convertView, parent);
|
||||
|
||||
((TextView) v).setTextColor(getResources().getColorStateList(
|
||||
|
@ -377,8 +397,7 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
return v;
|
||||
}
|
||||
|
||||
public View getDropDownView(int position, View convertView,
|
||||
ViewGroup parent) {
|
||||
public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
View v = super.getDropDownView(position, convertView, parent);
|
||||
|
||||
((TextView) v).setTextColor(getResources().getColorStateList(
|
||||
|
@ -386,7 +405,6 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -394,9 +412,11 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
*/
|
||||
@Override
|
||||
public void onDirectoryClick(File directory) {
|
||||
// invalidate checked state when navigating directories
|
||||
MenuItem selectAll = mOptionsMenu.findItem(R.id.action_select_all);
|
||||
setSelectAllMenuItem(selectAll, false);
|
||||
if(!mLocalFolderPickerMode) {
|
||||
// invalidate checked state when navigating directories
|
||||
MenuItem selectAll = mOptionsMenu.findItem(R.id.action_select_all);
|
||||
setSelectAllMenuItem(selectAll, false);
|
||||
}
|
||||
|
||||
pushDirname(directory);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -419,6 +439,14 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
return mCurrentDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isFolderPickerMode() {
|
||||
return mLocalFolderPickerMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs corresponding action when user presses 'Cancel' or 'Upload' button
|
||||
*
|
||||
|
@ -432,7 +460,17 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
finish();
|
||||
|
||||
} else if (v.getId() == R.id.upload_files_btn_upload) {
|
||||
new CheckAvailableSpaceTask().execute(mBehaviourSpinner.getSelectedItemPosition()==0);
|
||||
if(mLocalFolderPickerMode) {
|
||||
Intent data = new Intent();
|
||||
if(mCurrentDir != null) {
|
||||
data.putExtra(EXTRA_CHOSEN_FILES, mCurrentDir.getAbsolutePath());
|
||||
}
|
||||
setResult(RESULT_OK, data);
|
||||
|
||||
finish();
|
||||
} else {
|
||||
new CheckAvailableSpaceTask().execute(mBehaviourSpinner.getSelectedItemPosition() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -445,7 +483,7 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
private class CheckAvailableSpaceTask extends AsyncTask<Boolean, Void, Boolean> {
|
||||
|
||||
/**
|
||||
* Updates the UI before trying the movement
|
||||
* Updates the UI before trying the movement.
|
||||
*/
|
||||
@Override
|
||||
protected void onPreExecute () {
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
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;
|
||||
|
@ -30,9 +29,11 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.design.widget.BottomNavigationView;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.Menu;
|
||||
|
@ -41,21 +42,22 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
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.jobs.FilesSyncJob;
|
||||
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.DisplayUtils;
|
||||
import com.owncloud.android.utils.AnalyticsUtils;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -74,8 +76,12 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
|
||||
private static final String SCREEN_NAME = "Uploads";
|
||||
|
||||
private static final String EXPERT_MODE = "expert_mode";
|
||||
|
||||
private UploadMessagesReceiver mUploadMessagesReceiver;
|
||||
|
||||
private Menu mMenu;
|
||||
|
||||
@Override
|
||||
public void showFiles(boolean onDeviceOnly) {
|
||||
super.showFiles(onDeviceOnly);
|
||||
|
@ -211,9 +217,14 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
} else {
|
||||
openDrawer();
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.action_retry_uploads:
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.retryFailedUploads(this, null, null);
|
||||
if (mMenu != null) {
|
||||
mMenu.removeItem(R.id.action_retry_uploads);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.action_clear_failed_uploads:
|
||||
|
@ -234,6 +245,19 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
uploadListFragment.updateUploads();
|
||||
break;
|
||||
|
||||
case R.id.action_force_rescan:
|
||||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setExact(1_000L)
|
||||
.setUpdateCurrent(false)
|
||||
.build()
|
||||
.schedule();
|
||||
|
||||
if (mMenu != null) {
|
||||
mMenu.removeItem(R.id.action_force_rescan);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -243,8 +267,14 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.upload_list_menu, menu);
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
if (appPrefs.getBoolean(EXPERT_MODE, false)) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.upload_list_menu, menu);
|
||||
mMenu = menu;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -252,17 +282,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
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
|
||||
);
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,8 +303,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
|
||||
} else {
|
||||
// already updated -> just retry!
|
||||
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
|
||||
requester.retryFailedUploads(this, account, UploadResult.CREDENTIAL_ERROR);
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -63,7 +63,6 @@ import com.owncloud.android.authentication.AuthenticatorActivity;
|
|||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.PushConfigurationState;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.lib.common.UserInfo;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
|
@ -410,16 +409,11 @@ public class UserInfoActivity extends FileActivity {
|
|||
contentResolver);
|
||||
syncedFolderProvider.deleteSyncFoldersForAccount(account);
|
||||
|
||||
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(
|
||||
contentResolver, getActivity());
|
||||
uploadsStorageManager.cancelPendingAutoUploadJobsForAccount(account);
|
||||
uploadsStorageManager.removeAccountUploads(account);
|
||||
|
||||
// disable daily backup
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(
|
||||
contentResolver);
|
||||
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
|
||||
ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
|
||||
"false");
|
||||
|
||||
|
@ -433,7 +427,7 @@ public class UserInfoActivity extends FileActivity {
|
|||
PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryDataPushString,
|
||||
PushConfigurationState.class);
|
||||
pushArbitraryData.setShouldBeDeleted(true);
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account, PushUtils.KEY_PUSH,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PushUtils.KEY_PUSH,
|
||||
gson.toJson(pushArbitraryData));
|
||||
EventBus.getDefault().post(new TokenPushEvent());
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/**
|
||||
* ownCloud Android client application
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author LukeOwncloud
|
||||
* @author masensio
|
||||
* Copyright (C) 2016 ownCloud Inc.
|
||||
* @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 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.
|
||||
* 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/>.
|
||||
* 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;
|
||||
|
||||
|
@ -26,7 +26,6 @@ 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;
|
||||
|
@ -34,7 +33,6 @@ 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.authentication.AccountUtils;
|
||||
|
@ -43,7 +41,6 @@ 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;
|
||||
|
@ -102,10 +99,10 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
|
||||
@Override
|
||||
public int compare(OCUpload upload1, OCUpload upload2) {
|
||||
if (upload1 == null){
|
||||
if (upload1 == null) {
|
||||
return -1;
|
||||
}
|
||||
if (upload2 == null){
|
||||
if (upload2 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (UploadStatus.UPLOAD_IN_PROGRESS.equals(upload1.getUploadStatus())) {
|
||||
|
@ -153,7 +150,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
mUploadGroups[0] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_current_uploads)) {
|
||||
@Override
|
||||
public void refresh() {
|
||||
items = mUploadsStorageManager.getCurrentAndPendingUploads();
|
||||
items = mUploadsStorageManager.getCurrentAndPendingUploadsForCurrentAccount();
|
||||
Arrays.sort(items, comparator);
|
||||
}
|
||||
|
||||
|
@ -165,7 +162,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
mUploadGroups[1] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_failed_uploads)) {
|
||||
@Override
|
||||
public void refresh() {
|
||||
items = mUploadsStorageManager.getFailedButNotDelayedUploads();
|
||||
items = mUploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount();
|
||||
Arrays.sort(items, comparator);
|
||||
}
|
||||
|
||||
|
@ -178,7 +175,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
mUploadGroups[2] = new UploadGroup(mParentActivity.getString(R.string.uploads_view_group_finished_uploads)) {
|
||||
@Override
|
||||
public void refresh() {
|
||||
items = mUploadsStorageManager.getFinishedUploads();
|
||||
items = mUploadsStorageManager.getFinishedUploadsForCurrentAccount();
|
||||
Arrays.sort(items, comparator);
|
||||
}
|
||||
|
||||
|
@ -292,33 +289,33 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
/// ... unbind the old progress bar, if any; ...
|
||||
if (mProgressListener != null) {
|
||||
binder.removeDatatransferProgressListener(
|
||||
mProgressListener,
|
||||
mProgressListener.getUpload() // the one that was added
|
||||
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
|
||||
mProgressListener,
|
||||
upload
|
||||
);
|
||||
|
||||
} else {
|
||||
/// not really uploading; stop listening progress if view is reused!
|
||||
if (convertView != null &&
|
||||
mProgressListener != null &&
|
||||
mProgressListener.isWrapping(progressBar)) {
|
||||
mProgressListener.isWrapping(progressBar)) {
|
||||
binder.removeDatatransferProgressListener(
|
||||
mProgressListener,
|
||||
mProgressListener.getUpload() // the one that was added
|
||||
mProgressListener,
|
||||
mProgressListener.getUpload() // the one that was added
|
||||
);
|
||||
mProgressListener = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log_OC.w(
|
||||
TAG,
|
||||
"FileUploaderBinder not ready yet for upload " + upload.getRemotePath()
|
||||
TAG,
|
||||
"FileUploaderBinder not ready yet for upload " + upload.getRemotePath()
|
||||
);
|
||||
}
|
||||
uploadDateTextView.setVisibility(View.GONE);
|
||||
|
@ -336,13 +333,14 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
}
|
||||
statusTextView.setText(status);
|
||||
|
||||
/// bind listeners to perform actions
|
||||
/// 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_action_cancel_grey);
|
||||
rightButton.setVisibility(View.VISIBLE);
|
||||
rightButton.setOnClickListener(new OnClickListener() {
|
||||
rightButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FileUploader.FileUploaderBinder uploaderBinder = mParentActivity.getFileUploaderBinder();
|
||||
|
@ -357,7 +355,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
//Delete
|
||||
rightButton.setImageResource(R.drawable.ic_action_delete_grey);
|
||||
rightButton.setVisibility(View.VISIBLE);
|
||||
rightButton.setOnClickListener(new OnClickListener() {
|
||||
rightButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mUploadsStorageManager.removeUpload(upload);
|
||||
|
@ -368,41 +366,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
} 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);
|
||||
}
|
||||
|
||||
view.setOnClickListener(null);
|
||||
|
||||
/// Set icon or thumbnail
|
||||
ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
|
||||
|
@ -509,7 +474,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
* the given upload.
|
||||
*
|
||||
* @param upload Upload to describe.
|
||||
* @return Text describing the status of the given upload.
|
||||
* @return Text describing the status of the given upload.
|
||||
*/
|
||||
private String getStatusText(OCUpload upload) {
|
||||
|
||||
|
@ -533,37 +498,37 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
switch (upload.getLastResult()) {
|
||||
case CREDENTIAL_ERROR:
|
||||
status = mParentActivity.getString(
|
||||
R.string.uploads_view_upload_status_failed_credentials_error
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
R.string.uploads_view_upload_status_waiting_for_wifi
|
||||
);
|
||||
break;
|
||||
case DELAYED_FOR_CHARGING:
|
||||
|
@ -572,32 +537,35 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
break;
|
||||
case CONFLICT_ERROR:
|
||||
status = mParentActivity.getString(
|
||||
R.string.uploads_view_upload_status_conflict
|
||||
R.string.uploads_view_upload_status_conflict
|
||||
);
|
||||
break;
|
||||
case SERVICE_INTERRUPTED:
|
||||
status = mParentActivity.getString(
|
||||
R.string.uploads_view_upload_status_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
|
||||
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
|
||||
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);
|
||||
status = mParentActivity.getString(R.string.uploads_view_upload_status_succeeded);
|
||||
break;
|
||||
case MAINTENANCE_MODE:
|
||||
status = mParentActivity.getString(R.string.maintenance_mode);
|
||||
break;
|
||||
case LOCK_FAILED:
|
||||
status = mParentActivity.getString(R.string.lock_failed);
|
||||
break;
|
||||
default:
|
||||
status = "Naughty devs added a new fail result but no description for the user";
|
||||
break;
|
||||
|
@ -747,8 +715,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
public boolean isWrapping(ProgressBar progressBar) {
|
||||
ProgressBar wrappedProgressBar = mProgressBar.get();
|
||||
return (
|
||||
wrappedProgressBar != null &&
|
||||
wrappedProgressBar == progressBar // on purpose; don't replace with equals
|
||||
wrappedProgressBar != null &&
|
||||
wrappedProgressBar == progressBar // on purpose; don't replace with equals
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import com.owncloud.android.utils.FileStorageUtils;
|
|||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -58,10 +59,12 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
|
|||
|
||||
private Context mContext;
|
||||
private File[] mFiles = null;
|
||||
private Vector<File> mFilesAll = new Vector<File>();
|
||||
private Vector<File> mFilesAll = new Vector<>();
|
||||
private boolean mLocalFolderPicker;
|
||||
|
||||
public LocalFileListAdapter(File directory, Context context) {
|
||||
public LocalFileListAdapter(boolean localFolderPickerMode, File directory, Context context) {
|
||||
mContext = context;
|
||||
mLocalFolderPicker = localFolderPickerMode;
|
||||
|
||||
// Read sorting order, default to sort by name ascending
|
||||
FileStorageUtils.mSortOrder = PreferenceManager.getSortOrder(context);
|
||||
|
@ -272,7 +275,11 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
|
|||
* @param directory New file to adapt. Can be NULL, meaning "no content to adapt".
|
||||
*/
|
||||
public void swapDirectory(final File directory) {
|
||||
mFiles = (directory != null ? directory.listFiles() : null);
|
||||
if(mLocalFolderPicker) {
|
||||
mFiles = (directory != null ? getFolders(directory) : null);
|
||||
} else {
|
||||
mFiles = (directory != null ? directory.listFiles() : null);
|
||||
}
|
||||
if (mFiles != null) {
|
||||
Arrays.sort(mFiles, new Comparator<File>() {
|
||||
@Override
|
||||
|
@ -288,7 +295,6 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
|
|||
private int compareNames(File lhs, File rhs) {
|
||||
return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
mFiles = FileStorageUtils.sortLocalFolder(mFiles);
|
||||
|
@ -317,6 +323,15 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private File[] getFolders(final File directory) {
|
||||
return directory.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return file.isDirectory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void filter(String text){
|
||||
if(text.isEmpty()){
|
||||
mFiles = mFilesAll.toArray(new File[1]);
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2016 Andy Scherzinger
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2016 Andy Scherzinger
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.adapter;
|
||||
|
@ -29,10 +29,12 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.utils.ThemeUtils;
|
||||
|
@ -44,7 +46,7 @@ import java.util.List;
|
|||
/**
|
||||
* Adapter to display all auto-synced folders and/or instant upload media folders.
|
||||
*/
|
||||
public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAdapter.MainViewHolder> {
|
||||
public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SyncedFolderAdapter.MainViewHolder> {
|
||||
|
||||
private final Context mContext;
|
||||
private final int mGridWidth;
|
||||
|
@ -53,19 +55,20 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
private final List<SyncedFolderDisplayItem> mSyncFolderItems;
|
||||
private final boolean mLight;
|
||||
|
||||
public FolderSyncAdapter(Context context, int gridWidth, ClickListener listener, boolean light) {
|
||||
public SyncedFolderAdapter(Context context, int gridWidth, ClickListener listener, boolean light) {
|
||||
mContext = context;
|
||||
mGridWidth = gridWidth;
|
||||
mGridTotal = gridWidth * 2;
|
||||
mListener = listener;
|
||||
mSyncFolderItems = new ArrayList<>();
|
||||
mLight = light;
|
||||
|
||||
shouldShowHeadersForEmptySections(true);
|
||||
}
|
||||
|
||||
public void setSyncFolderItems(List<SyncedFolderDisplayItem> syncFolderItems) {
|
||||
mSyncFolderItems.clear();
|
||||
mSyncFolderItems.addAll(syncFolderItems);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderItem) {
|
||||
|
@ -73,6 +76,16 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) {
|
||||
mSyncFolderItems.add(syncFolderItem);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void removeItem(int section) {
|
||||
mSyncFolderItems.remove(section);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return mSyncFolderItems.size();
|
||||
|
@ -87,18 +100,39 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
}
|
||||
}
|
||||
|
||||
public SyncedFolderDisplayItem get(int section) {
|
||||
return mSyncFolderItems.get(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(final MainViewHolder holder, final int section) {
|
||||
holder.mainHeaderContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
holder.title.setText(mSyncFolderItems.get(section).getFolderName());
|
||||
|
||||
if (MediaFolderType.VIDEO == mSyncFolderItems.get(section).getType()) {
|
||||
holder.type.setImageResource(R.drawable.ic_video_18dp);
|
||||
} else if (MediaFolderType.IMAGE == mSyncFolderItems.get(section).getType()) {
|
||||
holder.type.setImageResource(R.drawable.ic_image_18dp);
|
||||
} else {
|
||||
holder.type.setImageResource(R.drawable.ic_folder_star_18dp);
|
||||
}
|
||||
|
||||
holder.syncStatusButton.setVisibility(View.VISIBLE);
|
||||
holder.syncStatusButton.setTag(section);
|
||||
holder.syncStatusButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled());
|
||||
setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
|
||||
mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section));
|
||||
}
|
||||
holder.syncStatusButton.setOnClickListener(v -> {
|
||||
mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled());
|
||||
setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
|
||||
mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section));
|
||||
});
|
||||
setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
|
||||
|
||||
holder.syncStatusButton.setVisibility(View.VISIBLE);
|
||||
holder.syncStatusButton.setTag(section);
|
||||
holder.syncStatusButton.setOnClickListener(v -> {
|
||||
mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled());
|
||||
setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
|
||||
mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section));
|
||||
});
|
||||
setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
|
||||
|
||||
|
@ -107,18 +141,14 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
} else {
|
||||
holder.menuButton.setVisibility(View.VISIBLE);
|
||||
holder.menuButton.setTag(section);
|
||||
holder.menuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mListener.onSyncFolderSettingsClick(section, mSyncFolderItems.get(section));
|
||||
}
|
||||
});
|
||||
holder.menuButton.setOnClickListener(v -> mListener.onSyncFolderSettingsClick(section,
|
||||
mSyncFolderItems.get(section)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(MainViewHolder holder, int section, int relativePosition, int absolutePosition) {
|
||||
|
||||
if (mSyncFolderItems.get(section).getFilePaths() != null) {
|
||||
File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition));
|
||||
|
||||
|
@ -148,14 +178,6 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
holder.counterBar.setVisibility(View.GONE);
|
||||
holder.thumbnailDarkener.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
//holder.itemView.setTag(String.format(Locale.getDefault(), "%d:%d:%d", section, relativePos, absolutePos));
|
||||
//holder.itemView.setOnClickListener(this);
|
||||
} else {
|
||||
holder.itemView.setTag(relativePosition % mGridWidth);
|
||||
holder.counterValue.setText(Long.toString(0));
|
||||
holder.counterBar.setVisibility(View.VISIBLE);
|
||||
holder.thumbnailDarkener.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +185,7 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(
|
||||
viewType == VIEW_TYPE_HEADER ?
|
||||
R.layout.folder_sync_item_header : R.layout.grid_sync_item, parent, false);
|
||||
R.layout.synced_folders_item_header : R.layout.grid_sync_item, parent, false);
|
||||
return new MainViewHolder(v);
|
||||
}
|
||||
|
||||
|
@ -175,16 +197,21 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
static class MainViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView image;
|
||||
private final TextView title;
|
||||
private final ImageView type;
|
||||
private final ImageButton menuButton;
|
||||
private final ImageButton syncStatusButton;
|
||||
private final LinearLayout counterBar;
|
||||
private final TextView counterValue;
|
||||
private final ImageView thumbnailDarkener;
|
||||
|
||||
private final RelativeLayout mainHeaderContainer;
|
||||
|
||||
private MainViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mainHeaderContainer = (RelativeLayout) itemView.findViewById(R.id.header_container);
|
||||
image = (ImageView) itemView.findViewById(R.id.thumbnail);
|
||||
title = (TextView) itemView.findViewById(R.id.title);
|
||||
type = (ImageView) itemView.findViewById(R.id.type);
|
||||
menuButton = (ImageButton) itemView.findViewById(R.id.settingsButton);
|
||||
syncStatusButton = (ImageButton) itemView.findViewById(R.id.syncStatusButton);
|
||||
counterBar = (LinearLayout) itemView.findViewById(R.id.counterLayout);
|
||||
|
@ -194,7 +221,7 @@ public class FolderSyncAdapter extends SectionedRecyclerViewAdapter<FolderSyncAd
|
|||
}
|
||||
|
||||
private void setSyncButtonActiveIcon(ImageButton syncStatusButton, boolean enabled) {
|
||||
if(enabled) {
|
||||
if (enabled) {
|
||||
syncStatusButton.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_cloud_sync_on,
|
||||
ThemeUtils.primaryColor()));
|
||||
} else {
|
|
@ -228,7 +228,9 @@ public class CopyAndUploadContentUrisTask extends AsyncTask<Object, Void, Result
|
|||
behaviour,
|
||||
mimeType,
|
||||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,10 @@ public class LoadingDialog extends DialogFragment {
|
|||
setCancelable(false);
|
||||
}
|
||||
|
||||
public LoadingDialog(String message) {
|
||||
this.mMessage = message;
|
||||
public static LoadingDialog newInstance(String message) {
|
||||
LoadingDialog loadingDialog = new LoadingDialog();
|
||||
loadingDialog.mMessage = message;
|
||||
return loadingDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -105,7 +105,7 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
|||
mView = inflater.inflate(R.layout.sorting_order_fragment, container, false);
|
||||
|
||||
setupDialogElements(mView);
|
||||
setupListeners(mView);
|
||||
setupListeners();
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
@ -208,10 +208,8 @@ public class SortingOrderDialogFragment extends DialogFragment {
|
|||
|
||||
/**
|
||||
* setup all listeners.
|
||||
*
|
||||
* @param view the parent view
|
||||
*/
|
||||
private void setupListeners(View view) {
|
||||
private void setupListeners() {
|
||||
mCancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.app.Dialog;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
@ -41,13 +42,19 @@ import android.view.ViewGroup;
|
|||
import android.widget.TextView;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.activity.FolderPickerActivity;
|
||||
import com.owncloud.android.ui.activity.UploadFilesActivity;
|
||||
import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
import com.owncloud.android.utils.ThemeUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID;
|
||||
|
||||
/**
|
||||
* Dialog to show the preferences/configuration of a synced folder allowing the user to change the different parameters.
|
||||
*/
|
||||
|
@ -57,6 +64,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
public static final String SYNCED_FOLDER_PARCELABLE = "SyncedFolderParcelable";
|
||||
private static final String BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE";
|
||||
public static final int REQUEST_CODE__SELECT_REMOTE_FOLDER = 0;
|
||||
public static final int REQUEST_CODE__SELECT_LOCAL_FOLDER = 1;
|
||||
|
||||
private CharSequence[] mUploadBehaviorItemStrings;
|
||||
|
||||
|
@ -67,6 +75,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
private AppCompatCheckBox mUploadUseSubfoldersCheckbox;
|
||||
private TextView mUploadBehaviorSummary;
|
||||
private TextView mLocalFolderPath;
|
||||
private TextView mLocalFolderSummary;
|
||||
private TextView mRemoteFolderSummary;
|
||||
|
||||
private SyncedFolderParcelable mSyncedFolder;
|
||||
|
@ -85,7 +94,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
Bundle args = new Bundle();
|
||||
args.putParcelable(SYNCED_FOLDER_PARCELABLE, new SyncedFolderParcelable(syncedFolder, section));
|
||||
dialogFragment.setArguments(args);
|
||||
dialogFragment.setStyle(STYLE_NORMAL,R.style.Theme_ownCloud_Dialog);
|
||||
dialogFragment.setStyle(STYLE_NORMAL, R.style.Theme_ownCloud_Dialog);
|
||||
|
||||
return dialogFragment;
|
||||
}
|
||||
|
@ -116,7 +125,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Log_OC.d(TAG, "onCreateView, savedInstanceState is " + savedInstanceState);
|
||||
|
||||
mView = inflater.inflate(R.layout.folder_sync_settings_layout, container, false);
|
||||
mView = inflater.inflate(R.layout.synced_folders_settings_layout, container, false);
|
||||
|
||||
setupDialogElements(mView);
|
||||
setupListeners(mView);
|
||||
|
@ -132,20 +141,49 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
private void setupDialogElements(View view) {
|
||||
int accentColor = ThemeUtils.primaryAccentColor();
|
||||
|
||||
if (mSyncedFolder.getType().getId() > MediaFolderType.CUSTOM.getId()) {
|
||||
// hide local folder chooser and delete for non-custom folders
|
||||
view.findViewById(R.id.local_folder_container).setVisibility(View.GONE);
|
||||
view.findViewById(R.id.delete).setVisibility(View.GONE);
|
||||
} else if (mSyncedFolder.getId() <= UNPERSISTED_ID) {
|
||||
// Hide delete/enabled for unpersisted custom folders
|
||||
view.findViewById(R.id.delete).setVisibility(View.GONE);
|
||||
view.findViewById(R.id.sync_enabled).setVisibility(View.GONE);
|
||||
|
||||
// auto set custom folder to enabled
|
||||
mSyncedFolder.setEnabled(true);
|
||||
|
||||
// switch text to create headline
|
||||
((TextView) view.findViewById(R.id.synced_folders_settings_title))
|
||||
.setText(R.string.autoupload_create_new_custom_folder);
|
||||
|
||||
// disable save button
|
||||
view.findViewById(R.id.save).setEnabled(false);
|
||||
} else {
|
||||
view.findViewById(R.id.local_folder_container).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// find/saves UI elements
|
||||
mEnabledSwitch = (SwitchCompat) view.findViewById(R.id.sync_enabled);
|
||||
ThemeUtils.tintSwitch(mEnabledSwitch, accentColor);
|
||||
|
||||
mLocalFolderPath = (TextView) view.findViewById(R.id.folder_sync_settings_local_folder_path);
|
||||
mLocalFolderPath = (TextView) view.findViewById(R.id.synced_folders_settings_local_folder_path);
|
||||
|
||||
mLocalFolderSummary = (TextView) view.findViewById(R.id.local_folder_summary);
|
||||
mRemoteFolderSummary = (TextView) view.findViewById(R.id.remote_folder_summary);
|
||||
|
||||
mUploadOnWifiCheckbox = (AppCompatCheckBox) view.findViewById(R.id.setting_instant_upload_on_wifi_checkbox);
|
||||
ThemeUtils.tintCheckbox(mUploadOnWifiCheckbox, accentColor);
|
||||
|
||||
mUploadOnChargingCheckbox = (AppCompatCheckBox) view.findViewById(
|
||||
R.id.setting_instant_upload_on_charging_checkbox);
|
||||
ThemeUtils.tintCheckbox(mUploadOnChargingCheckbox, accentColor);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
view.findViewById(R.id.setting_instant_upload_on_charging_container).setVisibility(View.GONE);
|
||||
} else {
|
||||
view.findViewById(R.id.setting_instant_upload_on_charging_container).setVisibility(View.VISIBLE);
|
||||
|
||||
mUploadOnChargingCheckbox = (AppCompatCheckBox) view.findViewById(
|
||||
R.id.setting_instant_upload_on_charging_checkbox);
|
||||
ThemeUtils.tintCheckbox(mUploadOnChargingCheckbox, accentColor);
|
||||
}
|
||||
|
||||
mUploadUseSubfoldersCheckbox = (AppCompatCheckBox) view.findViewById(
|
||||
R.id.setting_instant_upload_path_use_subfolders_checkbox);
|
||||
|
@ -161,18 +199,30 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
|
||||
// Set values
|
||||
setEnabled(mSyncedFolder.getEnabled());
|
||||
mLocalFolderPath.setText(
|
||||
DisplayUtils.createTextWithSpan(
|
||||
String.format(
|
||||
getString(R.string.folder_sync_preferences_folder_path),
|
||||
mSyncedFolder.getLocalPath()),
|
||||
mSyncedFolder.getFolderName(),
|
||||
new StyleSpan(Typeface.BOLD)));
|
||||
|
||||
mRemoteFolderSummary.setText(mSyncedFolder.getRemotePath());
|
||||
if (mSyncedFolder.getLocalPath() != null && mSyncedFolder.getLocalPath().length() > 0) {
|
||||
mLocalFolderPath.setText(
|
||||
DisplayUtils.createTextWithSpan(
|
||||
String.format(
|
||||
getString(R.string.synced_folders_preferences_folder_path),
|
||||
mSyncedFolder.getLocalPath()),
|
||||
mSyncedFolder.getFolderName(),
|
||||
new StyleSpan(Typeface.BOLD)));
|
||||
mLocalFolderSummary.setText(mSyncedFolder.getLocalPath());
|
||||
} else {
|
||||
mLocalFolderSummary.setText(R.string.choose_local_folder);
|
||||
}
|
||||
|
||||
if (mSyncedFolder.getLocalPath() != null && mSyncedFolder.getLocalPath().length() > 0) {
|
||||
mRemoteFolderSummary.setText(mSyncedFolder.getRemotePath());
|
||||
} else {
|
||||
mRemoteFolderSummary.setText(R.string.choose_remote_folder);
|
||||
}
|
||||
|
||||
mUploadOnWifiCheckbox.setChecked(mSyncedFolder.getWifiOnly());
|
||||
mUploadOnChargingCheckbox.setChecked(mSyncedFolder.getChargingOnly());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
mUploadOnChargingCheckbox.setChecked(mSyncedFolder.getChargingOnly());
|
||||
}
|
||||
mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.getSubfolderByDate());
|
||||
|
||||
mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]);
|
||||
|
@ -198,6 +248,35 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
public void setRemoteFolderSummary(String path) {
|
||||
mSyncedFolder.setRemotePath(path);
|
||||
mRemoteFolderSummary.setText(path);
|
||||
checkAndUpdateSaveButtonState();
|
||||
}
|
||||
|
||||
/**
|
||||
* set (new) local path on activity result of the folder picker activity. The result gets originally propagated
|
||||
* to the underlying activity since the picker is an activity and the result can't get passed to the dialog
|
||||
* fragment directly.
|
||||
*
|
||||
* @param path the remote path to be set
|
||||
*/
|
||||
public void setLocalFolderSummary(String path) {
|
||||
mSyncedFolder.setLocalPath(path);
|
||||
mLocalFolderSummary.setText(path);
|
||||
mLocalFolderPath.setText(
|
||||
DisplayUtils.createTextWithSpan(
|
||||
String.format(
|
||||
getString(R.string.synced_folders_preferences_folder_path),
|
||||
mSyncedFolder.getLocalPath()),
|
||||
new File(mSyncedFolder.getLocalPath()).getName(),
|
||||
new StyleSpan(Typeface.BOLD)));
|
||||
checkAndUpdateSaveButtonState();
|
||||
}
|
||||
|
||||
private void checkAndUpdateSaveButtonState() {
|
||||
if (mSyncedFolder.getLocalPath() != null && mSyncedFolder.getRemotePath() != null) {
|
||||
mView.findViewById(R.id.save).setEnabled(true);
|
||||
} else {
|
||||
mView.findViewById(R.id.save).setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,6 +287,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
private void setupListeners(View view) {
|
||||
mSave.setOnClickListener(new OnSyncedFolderSaveClickListener());
|
||||
mCancel.setOnClickListener(new OnSyncedFolderCancelClickListener());
|
||||
view.findViewById(R.id.delete).setOnClickListener(new OnSyncedFolderDeleteClickListener());
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_on_wifi_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
|
@ -218,14 +298,17 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_on_charging_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mSyncedFolder.setChargingOnly(!mSyncedFolder.getChargingOnly());
|
||||
mUploadOnChargingCheckbox.toggle();
|
||||
}
|
||||
});
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_on_charging_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mSyncedFolder.setChargingOnly(!mSyncedFolder.getChargingOnly());
|
||||
mUploadOnChargingCheckbox.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
|
@ -240,12 +323,19 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent action = new Intent(getActivity(), FolderPickerActivity.class);
|
||||
action.putExtra(
|
||||
FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.choose_remote_folder));
|
||||
getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_REMOTE_FOLDER);
|
||||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.local_folder_container).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent action = new Intent(getActivity(), UploadFilesActivity.class);
|
||||
action.putExtra(UploadFilesActivity.KEY_LOCAL_FOLDER_PICKER_MODE, true);
|
||||
getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_LOCAL_FOLDER);
|
||||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.sync_enabled).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -330,10 +420,20 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class OnSyncedFolderDeleteClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
((OnSyncedFolderPreferenceListener) getActivity()).onDeleteSyncedFolderPreference(mSyncedFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnSyncedFolderPreferenceListener {
|
||||
void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder);
|
||||
|
||||
void onCancelSyncedFolderPreference();
|
||||
|
||||
void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ package com.owncloud.android.ui.dialog.parcel;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
|
||||
|
@ -38,6 +39,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
private Boolean mEnabled = false;
|
||||
private Boolean mSubfolderByDate = false;
|
||||
private Integer mUploadAction;
|
||||
private MediaFolderType mType;
|
||||
private long mId;
|
||||
private String mAccount;
|
||||
private int mSection;
|
||||
|
@ -54,6 +56,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
mChargingOnly = syncedFolderDisplayItem.getChargingOnly();
|
||||
mEnabled = syncedFolderDisplayItem.isEnabled();
|
||||
mSubfolderByDate = syncedFolderDisplayItem.getSubfolderByDate();
|
||||
mType = syncedFolderDisplayItem.getType();
|
||||
mAccount = syncedFolderDisplayItem.getAccount();
|
||||
mUploadAction = syncedFolderDisplayItem.getUploadAction();
|
||||
mSection = section;
|
||||
|
@ -68,6 +71,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
mChargingOnly = read.readInt() != 0;
|
||||
mEnabled = read.readInt() != 0;
|
||||
mSubfolderByDate = read.readInt() != 0;
|
||||
mType = MediaFolderType.getById(read.readInt());
|
||||
mAccount = read.readString();
|
||||
mUploadAction = read.readInt();
|
||||
mSection = read.readInt();
|
||||
|
@ -83,6 +87,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
dest.writeInt(mChargingOnly ? 1 : 0);
|
||||
dest.writeInt(mEnabled ? 1 : 0);
|
||||
dest.writeInt(mSubfolderByDate ? 1 : 0);
|
||||
dest.writeInt(mType.getId());
|
||||
dest.writeString(mAccount);
|
||||
dest.writeInt(mUploadAction);
|
||||
dest.writeInt(mSection);
|
||||
|
@ -163,6 +168,14 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
this.mSubfolderByDate = mSubfolderByDate;
|
||||
}
|
||||
|
||||
public MediaFolderType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public void setType(MediaFolderType mType) {
|
||||
this.mType = mType;
|
||||
}
|
||||
|
||||
public Integer getUploadAction() {
|
||||
return mUploadAction;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -18,23 +17,19 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.services;
|
||||
package com.owncloud.android.ui.events;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
public class InitiateSyncedFolder {
|
||||
private final SyncedFolder syncedFolder;
|
||||
|
||||
/**
|
||||
* Handles shutdown procedure - basically just waits a little bit for all jobs to finish
|
||||
*/
|
||||
|
||||
public class ShutdownReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (MainApp.getSyncedFolderObserverService() != null) {
|
||||
MainApp.getSyncedFolderObserverService().onDestroy();
|
||||
}
|
||||
public InitiateSyncedFolder(SyncedFolder syncedFolder) {
|
||||
this.syncedFolder = syncedFolder;
|
||||
}
|
||||
|
||||
public SyncedFolder getSyncedFolder() {
|
||||
return syncedFolder;
|
||||
}
|
||||
}
|
|
@ -955,7 +955,7 @@ public class ExtendedListFragment extends Fragment
|
|||
maxColumnSize = maxColumnSizePortrait;
|
||||
}
|
||||
|
||||
if (mGridView.getNumColumns() > maxColumnSize) {
|
||||
if (mGridView != null && mGridView.getNumColumns() > maxColumnSize) {
|
||||
mGridView.setNumColumns(maxColumnSize);
|
||||
mGridView.invalidateViews();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import android.os.Bundle;
|
|||
import android.os.Environment;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
|
@ -89,7 +91,6 @@ public class LocalFileListFragment extends ExtendedListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -97,11 +98,19 @@ public class LocalFileListFragment extends ExtendedListFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Log_OC.i(TAG, "onCreateView() start");
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
|
||||
if(!mContainerActivity.isFolderPickerMode()) {
|
||||
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
setMessageForEmptyList(R.string.file_list_empty_headline, R.string.local_file_list_empty,
|
||||
R.drawable.ic_list_empty_folder, true);
|
||||
} else {
|
||||
setMessageForEmptyList(R.string.folder_list_empty_headline, R.string.local_folder_list_empty,
|
||||
R.drawable.ic_list_empty_folder, true);
|
||||
}
|
||||
|
||||
setSwipeEnabled(false); // Disable pull-to-refresh
|
||||
setFabEnabled(false); // Disable FAB
|
||||
setMessageForEmptyList(R.string.file_list_empty_headline, R.string.local_file_list_empty,
|
||||
R.drawable.ic_list_empty_folder, true);
|
||||
|
||||
Log_OC.i(TAG, "onCreateView() end");
|
||||
return v;
|
||||
}
|
||||
|
@ -115,11 +124,29 @@ public class LocalFileListFragment extends ExtendedListFragment {
|
|||
Log_OC.i(TAG, "onActivityCreated() start");
|
||||
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
|
||||
|
||||
mAdapter = new LocalFileListAdapter(
|
||||
mContainerActivity.isFolderPickerMode(),
|
||||
mContainerActivity.getInitialDirectory(),
|
||||
getActivity()
|
||||
);
|
||||
setListAdapter(mAdapter);
|
||||
|
||||
Log_OC.i(TAG, "onActivityCreated() stop");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (mContainerActivity.isFolderPickerMode()) {
|
||||
menu.removeItem(R.id.action_select_all);
|
||||
menu.removeItem(R.id.action_search);
|
||||
} else {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the file clicked over. Browses inside if it is a directory.
|
||||
|
@ -303,8 +330,7 @@ public class LocalFileListFragment extends ExtendedListFragment {
|
|||
* @param file
|
||||
*/
|
||||
void onFileClick(File file);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Callback method invoked when the parent activity
|
||||
* is fully created to get the directory to list firstly.
|
||||
|
@ -313,6 +339,13 @@ public class LocalFileListFragment extends ExtendedListFragment {
|
|||
*/
|
||||
File getInitialDirectory();
|
||||
|
||||
/**
|
||||
* config check if the list should behave in
|
||||
* folder picker mode only displaying folders but no files.
|
||||
*
|
||||
* @return true if folder picker mode, else false
|
||||
*/
|
||||
boolean isFolderPickerMode();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
|
|||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.services.ContactsImportJob;
|
||||
import com.owncloud.android.jobs.ContactsImportJob;
|
||||
import com.owncloud.android.ui.TextDrawable;
|
||||
import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
|
||||
import com.owncloud.android.ui.events.VCardToggleEvent;
|
||||
|
|
|
@ -49,9 +49,9 @@ import com.owncloud.android.R;
|
|||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.jobs.ContactsBackupJob;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.services.ContactsBackupJob;
|
||||
import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
|
||||
import com.owncloud.android.ui.activity.Preferences;
|
||||
import com.owncloud.android.ui.fragment.FileFragment;
|
||||
|
@ -338,7 +338,7 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
|
|||
contactsPreferenceActivity.getAccount());
|
||||
}
|
||||
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
|
||||
String.valueOf(bool));
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ public class FileOperationsHelper {
|
|||
openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
List<ResolveInfo> launchables = mFileActivity.getPackageManager().
|
||||
queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS);
|
||||
queryIntentActivities(openFileWithIntent, PackageManager.GET_RESOLVED_FILTER);
|
||||
|
||||
if (launchables != null && launchables.size() > 0) {
|
||||
try {
|
||||
|
|
|
@ -166,7 +166,9 @@ public class UriUploader {
|
|||
mBehaviour,
|
||||
null, // MIME type will be detected from file name
|
||||
false, // do not create parent folder if not existent
|
||||
UploadFileOperation.CREATED_BY_USER
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ public class PreviewTextFragment extends FileFragment {
|
|||
}
|
||||
|
||||
private void loadAndShowTextPreview() {
|
||||
mTextLoadTask = new TextLoadAsyncTask(new WeakReference<TextView>(mTextPreview));
|
||||
mTextLoadTask = new TextLoadAsyncTask(new WeakReference<>(mTextPreview));
|
||||
mTextLoadTask.execute(getFile().getStoragePath());
|
||||
}
|
||||
|
||||
|
@ -192,14 +192,12 @@ public class PreviewTextFragment extends FileFragment {
|
|||
* Reads the file to preview and shows its contents. Too critical to be anonymous.
|
||||
*/
|
||||
private class TextLoadAsyncTask extends AsyncTask<Object, Void, StringWriter> {
|
||||
private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
|
||||
private final WeakReference<TextView> mTextViewReference;
|
||||
|
||||
private TextLoadAsyncTask(WeakReference<TextView> textView) {
|
||||
mTextViewReference = textView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
// not used at the moment
|
||||
|
@ -454,7 +452,7 @@ public class PreviewTextFragment extends FileFragment {
|
|||
* @return 'True' if the file can be handled by the fragment.
|
||||
*/
|
||||
public static boolean canBePreviewed(OCFile file) {
|
||||
final List<String> unsupportedTypes = new LinkedList<String>();
|
||||
final List<String> unsupportedTypes = new LinkedList<>();
|
||||
unsupportedTypes.add("text/richtext");
|
||||
unsupportedTypes.add("text/rtf");
|
||||
unsupportedTypes.add("text/vnd.abc");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author David A. Velasco
|
||||
|
@ -23,7 +23,7 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.ExifInterface;
|
||||
import android.support.media.ExifInterface;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class BitmapUtils {
|
|||
* @param srcPath Absolute path to the file containing the image.
|
||||
* @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
|
||||
* @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
|
||||
* @return
|
||||
* @return decoded bitmap
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, int reqHeight) {
|
||||
|
||||
|
@ -106,6 +106,23 @@ public class BitmapUtils {
|
|||
return inSampleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* scales a given bitmap depending on the given size parameters.
|
||||
*
|
||||
* @param bitmap the bitmap to be scaled
|
||||
* @param px the target pixel size
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param max the max(height, width)
|
||||
* @return the scaled bitmap
|
||||
*/
|
||||
public static Bitmap scaleBitmap(Bitmap bitmap, float px, int width, int height, int max) {
|
||||
float scale = px / max;
|
||||
int w = Math.round(scale * width);
|
||||
int h = Math.round(scale * height);
|
||||
return Bitmap.createScaledBitmap(bitmap, w, h, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate bitmap according to EXIF orientation.
|
||||
* Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
|
||||
|
@ -172,7 +189,7 @@ public class BitmapUtils {
|
|||
* @param h Hue is specified as degrees in the range 0 - 360.
|
||||
* @param s Saturation is specified as a percentage in the range 1 - 100.
|
||||
* @param l Lumanance is specified as a percentage in the range 1 - 100.
|
||||
* @paran alpha the alpha value between 0 - 1
|
||||
* @param alpha the alpha value between 0 - 1
|
||||
* adapted from https://svn.codehaus.org/griffon/builders/gfxbuilder/tags/GFXBUILDER_0.2/
|
||||
* gfxbuilder-core/src/main/com/camick/awt/HSLColor.java
|
||||
*/
|
||||
|
@ -200,7 +217,7 @@ public class BitmapUtils {
|
|||
s /= 100f;
|
||||
l /= 100f;
|
||||
|
||||
float q = 0;
|
||||
float q;
|
||||
|
||||
if (l < 0.5) {
|
||||
q = l * (1 + s);
|
||||
|
|
|
@ -389,6 +389,11 @@ public class DisplayUtils {
|
|||
public static SpannableStringBuilder createTextWithSpan(String text, String spanText, StyleSpan style) {
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder(text);
|
||||
int start = text.lastIndexOf(spanText);
|
||||
|
||||
if (start < 0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
int end = start + spanText.length();
|
||||
sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
return sb;
|
||||
|
|
329
src/main/java/com/owncloud/android/utils/FilesSyncHelper.java
Normal file
|
@ -0,0 +1,329 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.utils;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.FilesystemDataProvider;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolder;
|
||||
import com.owncloud.android.datamodel.SyncedFolderProvider;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.jobs.FilesSyncJob;
|
||||
import com.owncloud.android.jobs.NContentObserverJob;
|
||||
|
||||
import org.lukhnos.nnio.file.FileVisitResult;
|
||||
import org.lukhnos.nnio.file.Files;
|
||||
import org.lukhnos.nnio.file.Path;
|
||||
import org.lukhnos.nnio.file.Paths;
|
||||
import org.lukhnos.nnio.file.SimpleFileVisitor;
|
||||
import org.lukhnos.nnio.file.attribute.BasicFileAttributes;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
Various utilities that make auto upload tick
|
||||
*/
|
||||
public class FilesSyncHelper {
|
||||
public static final String TAG = "FileSyncHelper";
|
||||
|
||||
public static final String GLOBAL = "global";
|
||||
public static final String SYNCEDFOLDERINITIATED = "syncedFolderIntitiated_";
|
||||
|
||||
public static int ContentSyncJobId = 315;
|
||||
|
||||
public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver);
|
||||
|
||||
Long currentTime = System.currentTimeMillis();
|
||||
double currentTimeInSeconds = currentTime / 1000.0;
|
||||
String currentTimeString = Long.toString((long) currentTimeInSeconds);
|
||||
|
||||
String syncedFolderInitiatedKey = SYNCEDFOLDERINITIATED + syncedFolder.getId();
|
||||
boolean dryRun = TextUtils.isEmpty(arbitraryDataProvider.getValue
|
||||
(GLOBAL, syncedFolderInitiatedKey));
|
||||
|
||||
if (MediaFolderType.IMAGE == syncedFolder.getType()) {
|
||||
if (dryRun) {
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey,
|
||||
currentTimeString);
|
||||
} else {
|
||||
FilesSyncHelper.insertContentIntoDB(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI
|
||||
, syncedFolder);
|
||||
FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
syncedFolder);
|
||||
}
|
||||
|
||||
} else if (MediaFolderType.VIDEO == syncedFolder.getType()) {
|
||||
|
||||
if (dryRun) {
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey,
|
||||
currentTimeString);
|
||||
} else {
|
||||
FilesSyncHelper.insertContentIntoDB(android.provider.MediaStore.Video.Media.INTERNAL_CONTENT_URI,
|
||||
syncedFolder);
|
||||
FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
syncedFolder);
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
|
||||
if (dryRun) {
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey,
|
||||
currentTimeString);
|
||||
} else {
|
||||
FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
|
||||
Path path = Paths.get(syncedFolder.getLocalPath());
|
||||
|
||||
String dateInitiated = arbitraryDataProvider.getValue(GLOBAL,
|
||||
syncedFolderInitiatedKey);
|
||||
|
||||
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
|
||||
|
||||
File file = path.toFile();
|
||||
if (attrs.lastModifiedTime().toMillis() >= Long.parseLong(dateInitiated) * 1000) {
|
||||
filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
|
||||
attrs.lastModifiedTime().toMillis(), file.isDirectory(), syncedFolder);
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Something went wrong while indexing files for auto upload " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void insertAllDBEntries(boolean skipCustom) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver);
|
||||
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if ((syncedFolder.isEnabled()) && ((MediaFolderType.CUSTOM != syncedFolder.getType()) || !skipCustom)) {
|
||||
insertAllDBEntriesForSyncedFolder(syncedFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) {
|
||||
final Context context = MainApp.getAppContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver);
|
||||
|
||||
Cursor cursor;
|
||||
int column_index_data;
|
||||
int column_index_date_modified;
|
||||
|
||||
final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
|
||||
|
||||
String contentPath;
|
||||
boolean isFolder;
|
||||
|
||||
String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DATE_MODIFIED};
|
||||
|
||||
String path = syncedFolder.getLocalPath();
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/%";
|
||||
} else {
|
||||
path = path + "%";
|
||||
}
|
||||
|
||||
String syncedFolderInitiatedKey = SYNCEDFOLDERINITIATED + syncedFolder.getId();
|
||||
String dateInitiated = arbitraryDataProvider.getValue(GLOBAL, syncedFolderInitiatedKey);
|
||||
|
||||
cursor = context.getContentResolver().query(uri, projection, MediaStore.MediaColumns.DATA + " LIKE ?",
|
||||
new String[]{path}, null);
|
||||
|
||||
if (cursor != null) {
|
||||
column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
||||
column_index_date_modified = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED);
|
||||
while (cursor.moveToNext()) {
|
||||
contentPath = cursor.getString(column_index_data);
|
||||
isFolder = new File(contentPath).isDirectory();
|
||||
if (cursor.getLong(column_index_date_modified) >= Long.parseLong(dateInitiated)) {
|
||||
filesystemDataProvider.storeOrUpdateFileValue(contentPath,
|
||||
cursor.getLong(column_index_date_modified), isFolder, syncedFolder);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void restartJobsIfNeeded() {
|
||||
final Context context = MainApp.getAppContext();
|
||||
|
||||
FileUploader.UploadRequester uploadRequester = new FileUploader.UploadRequester();
|
||||
|
||||
boolean accountExists;
|
||||
|
||||
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(context.getContentResolver(), context);
|
||||
OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
|
||||
|
||||
for (OCUpload failedUpload : failedUploads) {
|
||||
accountExists = false;
|
||||
|
||||
// check if accounts still exists
|
||||
for (Account account : AccountUtils.getAccounts(context)) {
|
||||
if (account.name.equals(failedUpload.getAccountName())) {
|
||||
accountExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accountExists) {
|
||||
uploadsStorageManager.removeUpload(failedUpload);
|
||||
}
|
||||
}
|
||||
|
||||
uploadRequester.retryFailedUploads(
|
||||
context,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static boolean isContentObserverJobScheduled() {
|
||||
JobScheduler js = MainApp.getAppContext().getSystemService(JobScheduler.class);
|
||||
List<JobInfo> jobs = js.getAllPendingJobs();
|
||||
|
||||
if (jobs == null || jobs.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < jobs.size(); i++) {
|
||||
if (jobs.get(i).getId() == ContentSyncJobId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void scheduleNJobs(boolean force) {
|
||||
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(MainApp.getAppContext().
|
||||
getContentResolver());
|
||||
|
||||
|
||||
boolean hasVideoFolders = false;
|
||||
boolean hasImageFolders = false;
|
||||
|
||||
if (syncedFolderProvider.getSyncedFolders() != null) {
|
||||
for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
|
||||
if (MediaFolderType.VIDEO == syncedFolder.getType()) {
|
||||
hasVideoFolders = true;
|
||||
} else if (MediaFolderType.IMAGE == syncedFolder.getType()) {
|
||||
hasImageFolders = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasImageFolders || hasVideoFolders) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
scheduleJobOnN(hasImageFolders, hasVideoFolders, force);
|
||||
}
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
cancelJobOnN();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void scheduleFilesSyncIfNeeded() {
|
||||
// always run this because it also allows us to perform retries of manual uploads
|
||||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setPeriodic(900000L, 300000L)
|
||||
.setUpdateCurrent(true)
|
||||
.build()
|
||||
.schedule();
|
||||
|
||||
scheduleNJobs(false);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
private static void cancelJobOnN() {
|
||||
JobScheduler jobScheduler = MainApp.getAppContext().getSystemService(JobScheduler.class);
|
||||
if (isContentObserverJobScheduled()) {
|
||||
jobScheduler.cancel(ContentSyncJobId);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
private static void scheduleJobOnN(boolean hasImageFolders, boolean hasVideoFolders,
|
||||
boolean force) {
|
||||
JobScheduler jobScheduler = MainApp.getAppContext().getSystemService(JobScheduler.class);
|
||||
|
||||
if ((hasImageFolders || hasVideoFolders) && (!isContentObserverJobScheduled() || force)) {
|
||||
JobInfo.Builder builder = new JobInfo.Builder(ContentSyncJobId, new ComponentName(MainApp.getAppContext(),
|
||||
NContentObserverJob.class.getName()));
|
||||
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(android.provider.MediaStore.
|
||||
Images.Media.INTERNAL_CONTENT_URI,
|
||||
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
|
||||
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.
|
||||
Images.Media.EXTERNAL_CONTENT_URI,
|
||||
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
|
||||
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(android.provider.MediaStore.
|
||||
Video.Media.INTERNAL_CONTENT_URI,
|
||||
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
|
||||
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.
|
||||
Video.Media.EXTERNAL_CONTENT_URI,
|
||||
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
|
||||
builder.setTriggerContentMaxDelay(1500);
|
||||
jobScheduler.schedule(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ public class MimeTypeUtil {
|
|||
} else if (isSharedViaUsers) {
|
||||
drawableId = R.drawable.shared_with_me_folder;
|
||||
} else {
|
||||
drawableId = R.drawable.ic_menu_archive;
|
||||
drawableId = R.drawable.folder;
|
||||
}
|
||||
|
||||
return ThemeUtils.tintDrawable(drawableId, ThemeUtils.primaryColor(account));
|
||||
|
@ -442,7 +442,7 @@ public class MimeTypeUtil {
|
|||
MIMETYPE_TO_ICON_MAPPING.put("application/yaml", R.drawable.file_code);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("application/zip", R.drawable.file_zip);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("database", R.drawable.file);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("httpd/unix-directory", R.drawable.ic_menu_archive);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("httpd/unix-directory", R.drawable.folder);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("image/svg+xml", R.drawable.file_image);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("image/vector", R.drawable.file_image);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("text/calendar", R.drawable.file_calendar);
|
||||
|
@ -456,7 +456,7 @@ public class MimeTypeUtil {
|
|||
MIMETYPE_TO_ICON_MAPPING.put("text/x-python", R.drawable.file_code);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("text/x-shellscript", R.drawable.file_code);
|
||||
MIMETYPE_TO_ICON_MAPPING.put("web", R.drawable.file_code);
|
||||
MIMETYPE_TO_ICON_MAPPING.put(MimeType.DIRECTORY, R.drawable.ic_menu_archive);
|
||||
MIMETYPE_TO_ICON_MAPPING.put(MimeType.DIRECTORY, R.drawable.folder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic
|
||||
* Copyright (C) 2017 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.utils;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.Device;
|
||||
import com.owncloud.android.MainApp;
|
||||
|
||||
/*
|
||||
Helper for setting up network and power receivers
|
||||
*/
|
||||
public class ReceiversHelper {
|
||||
|
||||
public static void registerNetworkChangeReceiver() {
|
||||
Context context = MainApp.getAppContext();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
|
||||
intentFilter.addAction("android.net.wifi.STATE_CHANGE");
|
||||
|
||||
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Device.getNetworkType(context).equals(JobRequest.NetworkType.UNMETERED)) {
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
|
||||
public static void registerPowerChangeReceiver() {
|
||||
Context context = MainApp.getAppContext();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
|
||||
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
|
||||
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ import android.support.v4.content.ContextCompat;
|
|||
import android.support.v4.content.res.ResourcesCompat;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.widget.CompoundButtonCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.AppCompatCheckBox;
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
|
@ -254,7 +255,7 @@ public class ThemeUtils {
|
|||
}
|
||||
|
||||
public static void tintCheckbox(AppCompatCheckBox checkBox, int color) {
|
||||
checkBox.setSupportButtonTintList(new ColorStateList(
|
||||
CompoundButtonCompat.setButtonTintList(checkBox, new ColorStateList(
|
||||
new int[][]{
|
||||
new int[]{-android.R.attr.state_checked},
|
||||
new int[]{android.R.attr.state_checked},
|
||||
|
|
|
@ -273,7 +273,9 @@ public class GridViewWithHeaderAndFooter extends GridView {
|
|||
Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth");
|
||||
numColumns.setAccessible(true);
|
||||
return numColumns.getInt(this);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 562 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 614 B |
Before Width: | Height: | Size: 569 B |
Before Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 412 B |