mirror of
https://github.com/nextcloud/android.git
synced 2024-12-20 07:52:18 +03:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
398e2f2d16
12 changed files with 329 additions and 267 deletions
2
.github/workflows/assembleFlavors.yml
vendored
2
.github/workflows/assembleFlavors.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
|
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
|
2
.github/workflows/detectWrongSettings.yml
vendored
2
.github/workflows/detectWrongSettings.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
|
2
.github/workflows/qa.yml
vendored
2
.github/workflows/qa.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
|
||||
if: ${{ steps.check-secrets.outputs.ok == 'true' }}
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
|
||||
if: ${{ steps.check-secrets.outputs.ok == 'true' }}
|
||||
with:
|
||||
distribution: "temurin"
|
||||
|
|
2
.github/workflows/screenShotTest.yml
vendored
2
.github/workflows/screenShotTest.yml
vendored
|
@ -40,7 +40,7 @@ jobs:
|
|||
~/.android/adb*
|
||||
key: avd-${{ matrix.api-level }}
|
||||
|
||||
- uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
|
||||
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
|
2
.github/workflows/unit-tests.yml
vendored
2
.github/workflows/unit-tests.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
|
|
@ -169,6 +169,9 @@
|
|||
<receiver
|
||||
android:name="com.nextcloud.client.jobs.NotificationWork$NotificationReceiver"
|
||||
android:exported="false" />
|
||||
<receiver
|
||||
android:name="com.owncloud.android.files.services.FileUploader$UploadNotificationActionReceiver"
|
||||
android:exported="false" />
|
||||
<receiver
|
||||
android:name="com.nextcloud.client.widget.DashboardWidgetProvider"
|
||||
android:exported="false">
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager
|
|||
import com.owncloud.android.datamodel.ThumbnailsCacheManager
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager
|
||||
import com.owncloud.android.db.OCUpload
|
||||
import com.owncloud.android.files.services.FileUploader
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
|
||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
||||
|
@ -197,6 +198,18 @@ class FilesUploadWorker(
|
|||
* adapted from [com.owncloud.android.files.services.FileUploader.notifyUploadStart]
|
||||
*/
|
||||
private fun createNotification(uploadFileOperation: UploadFileOperation) {
|
||||
val notificationActionIntent = Intent(context, FileUploader.UploadNotificationActionReceiver::class.java)
|
||||
notificationActionIntent.putExtra(FileUploader.EXTRA_ACCOUNT_NAME, uploadFileOperation.user.accountName)
|
||||
notificationActionIntent.putExtra(FileUploader.EXTRA_REMOTE_PATH, uploadFileOperation.remotePath)
|
||||
notificationActionIntent.action = FileUploader.ACTION_CANCEL_BROADCAST
|
||||
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
SecureRandom().nextInt(),
|
||||
notificationActionIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
notificationBuilder
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
|
@ -209,6 +222,8 @@ class FilesUploadWorker(
|
|||
uploadFileOperation.fileName
|
||||
)
|
||||
)
|
||||
.clearActions() // to make sure there is only one action
|
||||
.addAction(R.drawable.ic_action_cancel_grey, context.getString(R.string.common_cancel), pendingIntent)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
|
||||
|
@ -275,6 +290,7 @@ class FilesUploadWorker(
|
|||
.setAutoCancel(true)
|
||||
.setOngoing(false)
|
||||
.setProgress(0, 0, false)
|
||||
.clearActions()
|
||||
|
||||
val content = ErrorMessageAdapter.getErrorCauseMessage(uploadResult, uploadFileOperation, context.resources)
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import android.app.Notification;
|
|||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
@ -128,6 +129,10 @@ public class FileUploader extends Service
|
|||
public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
|
||||
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
|
||||
|
||||
public static final String EXTRA_ACCOUNT_NAME = "ACCOUNT_NAME";
|
||||
public static final String ACTION_CANCEL_BROADCAST = "CANCEL";
|
||||
public static final String ACTION_PAUSE_BROADCAST = "PAUSE";
|
||||
|
||||
private static final int FOREGROUND_SERVICE_ID = 411;
|
||||
|
||||
public static final String KEY_FILE = "FILE";
|
||||
|
@ -198,11 +203,13 @@ public class FileUploader extends Service
|
|||
private Notification mNotification;
|
||||
private Looper mServiceLooper;
|
||||
private ServiceHandler mServiceHandler;
|
||||
private IBinder mBinder;
|
||||
private static IBinder mBinder;
|
||||
private OwnCloudClient mUploadClient;
|
||||
private Account mCurrentAccount;
|
||||
private FileDataStorageManager mStorageManager;
|
||||
|
||||
private SecureRandom secureRandomGenerator = new SecureRandom();
|
||||
|
||||
@Inject UserAccountManager accountManager;
|
||||
@Inject UploadsStorageManager mUploadsStorageManager;
|
||||
@Inject ConnectivityService connectivityService;
|
||||
|
@ -233,6 +240,7 @@ public class FileUploader extends Service
|
|||
/**
|
||||
* Service initialization
|
||||
*/
|
||||
@SuppressFBWarnings("ST")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
@ -280,6 +288,7 @@ public class FileUploader extends Service
|
|||
/**
|
||||
* Service clean up
|
||||
*/
|
||||
@SuppressFBWarnings("ST")
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log_OC.v(TAG, "Destroying service");
|
||||
|
@ -708,6 +717,12 @@ public class FileUploader extends Service
|
|||
*/
|
||||
private void notifyUploadStart(UploadFileOperation upload) {
|
||||
// / create status notification with a progress bar
|
||||
Intent notificationActionIntent = new Intent(getApplicationContext(),UploadNotificationActionReceiver.class);
|
||||
notificationActionIntent.putExtra(EXTRA_ACCOUNT_NAME,upload.getUser().getAccountName());
|
||||
notificationActionIntent.putExtra(EXTRA_REMOTE_PATH,upload.getRemotePath());
|
||||
notificationActionIntent.setAction(ACTION_CANCEL_BROADCAST);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),secureRandomGenerator.nextInt(),notificationActionIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
mLastPercent = 0;
|
||||
mNotificationBuilder = NotificationUtils.newNotificationBuilder(this, viewThemeUtils);
|
||||
mNotificationBuilder
|
||||
|
@ -718,7 +733,10 @@ public class FileUploader extends Service
|
|||
.setProgress(100, 0, false)
|
||||
.setContentText(
|
||||
String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName())
|
||||
);
|
||||
)
|
||||
.clearActions() // to make sure there is only one action
|
||||
.addAction(R.drawable.ic_action_cancel_grey,getApplicationContext().getString(R.string.common_cancel),pendingIntent);
|
||||
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mNotificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD);
|
||||
|
@ -811,7 +829,8 @@ public class FileUploader extends Service
|
|||
.setContentTitle(getString(tickerId))
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(false)
|
||||
.setProgress(0, 0, false);
|
||||
.setProgress(0, 0, false)
|
||||
.clearActions();
|
||||
|
||||
content = ErrorMessageAdapter.getErrorCauseMessage(uploadResult, upload, getResources());
|
||||
|
||||
|
@ -859,7 +878,7 @@ public class FileUploader extends Service
|
|||
|
||||
mNotificationBuilder.setContentText(content);
|
||||
if (!uploadResult.isSuccess()) {
|
||||
mNotificationManager.notify((new SecureRandom()).nextInt(), mNotificationBuilder.build());
|
||||
mNotificationManager.notify(secureRandomGenerator.nextInt(), mNotificationBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1408,4 +1427,32 @@ public class FileUploader extends Service
|
|||
mService.stopSelf(msg.arg1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When cancel action in upload notification is pressed, cancel upload of item
|
||||
*/
|
||||
public static class UploadNotificationActionReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
|
||||
String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||
String action = intent.getAction();
|
||||
|
||||
if (ACTION_CANCEL_BROADCAST.equals(action)) {
|
||||
Log_OC.d(TAG, "Cancel broadcast received for file " + remotePath + " at " + System.currentTimeMillis());
|
||||
|
||||
if (accountName == null || remotePath == null) return;
|
||||
|
||||
FileUploaderBinder uploadBinder = (FileUploaderBinder) mBinder;
|
||||
uploadBinder.cancel(accountName, remotePath, null);
|
||||
}else if(ACTION_PAUSE_BROADCAST.equals(action)){
|
||||
|
||||
} else {
|
||||
Log_OC.d(TAG, "Unknown action to perform as UploadNotificationActionReceiver.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author Bartek Przybylski
|
||||
* @author David A. Velasco Copyright (C) 2012 Bartek Przybylski Copyright (C) 2016 ownCloud Inc.
|
||||
* <p>
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p/>
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.java.util.Optional;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.FileDownloader;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile;
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper activity which will be launched if keep-in-sync file will be modified by external application.
|
||||
*/
|
||||
public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
|
||||
/**
|
||||
* A nullable upload entry that must be removed when and if the conflict is resolved.
|
||||
*/
|
||||
public static final String EXTRA_CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID";
|
||||
/**
|
||||
* Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
|
||||
*/
|
||||
public static final String EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR";
|
||||
public static final String EXTRA_EXISTING_FILE = "EXISTING_FILE";
|
||||
|
||||
private static final String TAG = ConflictsResolveActivity.class.getSimpleName();
|
||||
|
||||
@Inject UploadsStorageManager uploadsStorageManager;
|
||||
|
||||
private long conflictUploadId;
|
||||
private OCFile existingFile;
|
||||
private OCFile newFile;
|
||||
private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||
protected OnConflictDecisionMadeListener listener;
|
||||
|
||||
public static Intent createIntent(OCFile file,
|
||||
User user,
|
||||
long conflictUploadId,
|
||||
Integer flag,
|
||||
Context context) {
|
||||
Intent intent = new Intent(context, ConflictsResolveActivity.class);
|
||||
if (flag != null) {
|
||||
intent.setFlags(intent.getFlags() | flag);
|
||||
}
|
||||
intent.putExtra(EXTRA_FILE, file);
|
||||
intent.putExtra(EXTRA_USER, user);
|
||||
intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID);
|
||||
existingFile = savedInstanceState.getParcelable(EXTRA_EXISTING_FILE);
|
||||
localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR);
|
||||
} else {
|
||||
conflictUploadId = getIntent().getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1);
|
||||
existingFile = getIntent().getParcelableExtra(EXTRA_EXISTING_FILE);
|
||||
localBehaviour = getIntent().getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
|
||||
}
|
||||
|
||||
OCUpload upload = uploadsStorageManager.getUploadById(conflictUploadId);
|
||||
|
||||
if (upload != null) {
|
||||
localBehaviour = upload.getLocalAction();
|
||||
}
|
||||
|
||||
// new file was modified locally in file system
|
||||
newFile = getFile();
|
||||
|
||||
listener = decision -> {
|
||||
OCFile file = newFile; // local file got changed, so either upload it or replace it again by server
|
||||
// version
|
||||
User user = getUser().orElseThrow(RuntimeException::new);
|
||||
switch (decision) {
|
||||
case CANCEL:
|
||||
// nothing to do
|
||||
break;
|
||||
case KEEP_LOCAL: // Upload
|
||||
FileUploader.uploadUpdateFile(
|
||||
getBaseContext(),
|
||||
user,
|
||||
file,
|
||||
localBehaviour,
|
||||
NameCollisionPolicy.OVERWRITE
|
||||
);
|
||||
|
||||
uploadsStorageManager.removeUpload(upload);
|
||||
break;
|
||||
case KEEP_BOTH: // Upload
|
||||
FileUploader.uploadUpdateFile(
|
||||
getBaseContext(),
|
||||
user,
|
||||
file,
|
||||
localBehaviour,
|
||||
NameCollisionPolicy.RENAME
|
||||
);
|
||||
|
||||
uploadsStorageManager.removeUpload(upload);
|
||||
break;
|
||||
case KEEP_SERVER: // Download
|
||||
if (!shouldDeleteLocal()) {
|
||||
// Overwrite local file
|
||||
Intent intent = new Intent(getBaseContext(), FileDownloader.class);
|
||||
intent.putExtra(FileDownloader.EXTRA_USER, getUser().orElseThrow(RuntimeException::new));
|
||||
intent.putExtra(FileDownloader.EXTRA_FILE, file);
|
||||
intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
|
||||
startService(intent);
|
||||
} else {
|
||||
uploadsStorageManager.removeUpload(upload);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
finish();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
|
||||
outState.putParcelable(EXTRA_EXISTING_FILE, existingFile);
|
||||
outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conflictDecisionMade(Decision decision) {
|
||||
listener.conflictDecisionMade(decision);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (getAccount() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newFile == null) {
|
||||
Log_OC.e(TAG, "No file received");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingFile == null) {
|
||||
// fetch info of existing file from server
|
||||
ReadFileRemoteOperation operation = new ReadFileRemoteOperation(newFile.getRemotePath());
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
RemoteOperationResult result = operation.execute(getAccount(), this);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
existingFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
|
||||
existingFile.setLastSyncDateForProperties(System.currentTimeMillis());
|
||||
|
||||
startDialog();
|
||||
} else {
|
||||
Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.getHttpCode());
|
||||
showErrorAndFinish();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG, "Error when trying to fetch remote file", e);
|
||||
showErrorAndFinish();
|
||||
}
|
||||
|
||||
|
||||
}).start();
|
||||
} else {
|
||||
startDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void startDialog() {
|
||||
Optional<User> userOptional = getUser();
|
||||
|
||||
if (!userOptional.isPresent()) {
|
||||
Log_OC.e(TAG, "User not present");
|
||||
showErrorAndFinish();
|
||||
}
|
||||
|
||||
// Check whether the file is contained in the current Account
|
||||
Fragment prev = getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
if (prev != null) {
|
||||
fragmentTransaction.remove(prev);
|
||||
}
|
||||
|
||||
if (existingFile != null && getStorageManager().fileExists(newFile.getRemotePath())) {
|
||||
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
|
||||
newFile,
|
||||
userOptional.get());
|
||||
dialog.show(fragmentTransaction, "conflictDialog");
|
||||
} else {
|
||||
// Account was changed to a different one - just finish
|
||||
Log_OC.e(TAG, "Account was changed, finishing");
|
||||
showErrorAndFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private void showErrorAndFinish() {
|
||||
runOnUiThread(() -> Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show());
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the local version of the files is to be deleted.
|
||||
*/
|
||||
private boolean shouldDeleteLocal() {
|
||||
return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author Bartek Przybylski
|
||||
* @author David A. Velasco Copyright (C) 2012 Bartek Przybylski Copyright (C) 2016 ownCloud Inc.
|
||||
* <p>
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* <p/>
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.nextcloud.client.account.User
|
||||
import com.nextcloud.utils.extensions.getParcelableArgument
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager
|
||||
import com.owncloud.android.db.OCUpload
|
||||
import com.owncloud.android.files.services.FileDownloader
|
||||
import com.owncloud.android.files.services.FileUploader
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy
|
||||
import com.owncloud.android.lib.common.utils.Log_OC
|
||||
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener
|
||||
import com.owncloud.android.utils.FileStorageUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Wrapper activity which will be launched if keep-in-sync file will be modified by external application.
|
||||
*/
|
||||
class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener {
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var uploadsStorageManager: UploadsStorageManager? = null
|
||||
|
||||
private var conflictUploadId: Long = 0
|
||||
private var existingFile: OCFile? = null
|
||||
private var newFile: OCFile? = null
|
||||
private var localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET
|
||||
|
||||
@JvmField
|
||||
var listener: OnConflictDecisionMadeListener? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
getArguments(savedInstanceState)
|
||||
|
||||
val upload = uploadsStorageManager?.getUploadById(conflictUploadId)
|
||||
if (upload != null) {
|
||||
localBehaviour = upload.localAction
|
||||
}
|
||||
|
||||
// new file was modified locally in file system
|
||||
newFile = file
|
||||
setupOnConflictDecisionMadeListener(upload)
|
||||
}
|
||||
|
||||
private fun getArguments(savedInstanceState: Bundle?) {
|
||||
if (savedInstanceState != null) {
|
||||
conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID)
|
||||
existingFile = savedInstanceState.getParcelableArgument(EXTRA_EXISTING_FILE, OCFile::class.java)
|
||||
localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR)
|
||||
} else {
|
||||
conflictUploadId = intent.getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1)
|
||||
existingFile = intent.getParcelableExtra(EXTRA_EXISTING_FILE)
|
||||
localBehaviour = intent.getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupOnConflictDecisionMadeListener(upload: OCUpload?) {
|
||||
listener = OnConflictDecisionMadeListener { decision: Decision? ->
|
||||
val file = newFile // local file got changed, so either upload it or replace it again by server
|
||||
// version
|
||||
val user = user.orElseThrow { RuntimeException() }
|
||||
when (decision) {
|
||||
Decision.CANCEL -> {}
|
||||
Decision.KEEP_LOCAL -> {
|
||||
FileUploader.uploadUpdateFile(
|
||||
baseContext,
|
||||
user,
|
||||
file,
|
||||
localBehaviour,
|
||||
NameCollisionPolicy.OVERWRITE
|
||||
)
|
||||
uploadsStorageManager!!.removeUpload(upload)
|
||||
}
|
||||
|
||||
Decision.KEEP_BOTH -> {
|
||||
FileUploader.uploadUpdateFile(
|
||||
baseContext,
|
||||
user,
|
||||
file,
|
||||
localBehaviour,
|
||||
NameCollisionPolicy.RENAME
|
||||
)
|
||||
uploadsStorageManager!!.removeUpload(upload)
|
||||
}
|
||||
|
||||
Decision.KEEP_SERVER -> if (!shouldDeleteLocal()) {
|
||||
// Overwrite local file
|
||||
val intent = Intent(baseContext, FileDownloader::class.java)
|
||||
intent.putExtra(FileDownloader.EXTRA_USER, getUser().orElseThrow { RuntimeException() })
|
||||
intent.putExtra(FileDownloader.EXTRA_FILE, file)
|
||||
intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
|
||||
startService(intent)
|
||||
} else {
|
||||
uploadsStorageManager!!.removeUpload(upload)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
|
||||
outState.putParcelable(EXTRA_EXISTING_FILE, existingFile)
|
||||
outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour)
|
||||
}
|
||||
|
||||
override fun conflictDecisionMade(decision: Decision) {
|
||||
listener?.conflictDecisionMade(decision)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (account == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
if (newFile == null) {
|
||||
Log_OC.e(TAG, "No file received")
|
||||
finish()
|
||||
return
|
||||
}
|
||||
if (existingFile == null) {
|
||||
// fetch info of existing file from server
|
||||
val operation = ReadFileRemoteOperation(newFile!!.remotePath)
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
Thread {
|
||||
try {
|
||||
val result = operation.execute(account, this)
|
||||
if (result.isSuccess) {
|
||||
existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile)
|
||||
existingFile?.lastSyncDateForProperties = System.currentTimeMillis()
|
||||
startDialog()
|
||||
} else {
|
||||
Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode)
|
||||
showErrorAndFinish()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log_OC.e(TAG, "Error when trying to fetch remote file", e)
|
||||
showErrorAndFinish()
|
||||
}
|
||||
}.start()
|
||||
} else {
|
||||
startDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startDialog() {
|
||||
val userOptional = user
|
||||
if (!userOptional.isPresent) {
|
||||
Log_OC.e(TAG, "User not present")
|
||||
showErrorAndFinish()
|
||||
}
|
||||
|
||||
// Check whether the file is contained in the current Account
|
||||
val prev = supportFragmentManager.findFragmentByTag("conflictDialog")
|
||||
val fragmentTransaction = supportFragmentManager.beginTransaction()
|
||||
if (prev != null) {
|
||||
fragmentTransaction.remove(prev)
|
||||
}
|
||||
if (existingFile != null && storageManager.fileExists(newFile!!.remotePath)) {
|
||||
val dialog = ConflictsResolveDialog.newInstance(
|
||||
existingFile,
|
||||
newFile,
|
||||
userOptional.get()
|
||||
)
|
||||
dialog.show(fragmentTransaction, "conflictDialog")
|
||||
} else {
|
||||
// Account was changed to a different one - just finish
|
||||
Log_OC.e(TAG, "Account was changed, finishing")
|
||||
showErrorAndFinish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorAndFinish() {
|
||||
runOnUiThread { Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show() }
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the local version of the files is to be deleted.
|
||||
*/
|
||||
private fun shouldDeleteLocal(): Boolean {
|
||||
return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* A nullable upload entry that must be removed when and if the conflict is resolved.
|
||||
*/
|
||||
const val EXTRA_CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID"
|
||||
|
||||
/**
|
||||
* Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
|
||||
*/
|
||||
const val EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR"
|
||||
const val EXTRA_EXISTING_FILE = "EXISTING_FILE"
|
||||
private val TAG = ConflictsResolveActivity::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun createIntent(
|
||||
file: OCFile?,
|
||||
user: User?,
|
||||
conflictUploadId: Long,
|
||||
flag: Int?,
|
||||
context: Context?
|
||||
): Intent {
|
||||
val intent = Intent(context, ConflictsResolveActivity::class.java)
|
||||
if (flag != null) {
|
||||
intent.flags = intent.flags or flag
|
||||
}
|
||||
intent.putExtra(EXTRA_FILE, file)
|
||||
intent.putExtra(EXTRA_USER, user)
|
||||
intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
}
|
|
@ -650,6 +650,8 @@
|
|||
<string name="remove_e2e">您可以在此客戶端上本機移除端到端加密</string>
|
||||
<string name="remove_e2e_message">您可以在此客戶端上移除本機端到端加密。已加密的檔案將會保留在伺服器上,但不會再同步到此電腦。</string>
|
||||
<string name="remove_fail_msg">刪除失敗</string>
|
||||
<string name="remove_local_account">移除本機帳號</string>
|
||||
<string name="remove_local_account_details">從裝置移除帳號並刪除所有本機檔案</string>
|
||||
<string name="remove_notification_failed">移除通知失敗</string>
|
||||
<string name="remove_push_notification">移除</string>
|
||||
<string name="remove_success_msg">已刪除</string>
|
||||
|
@ -657,6 +659,8 @@
|
|||
<string name="rename_local_fail_msg">無法重新命名本機副本,請嘗試不同的名稱</string>
|
||||
<string name="rename_server_fail_msg">無法重新命名,該名稱已被使用</string>
|
||||
<string name="request_account_deletion">請求刪除帳號</string>
|
||||
<string name="request_account_deletion_button">請求刪除</string>
|
||||
<string name="request_account_deletion_details">請求服務提供者永久刪除帳號</string>
|
||||
<string name="reshare_not_allowed">不允許重新分享</string>
|
||||
<string name="resharing_is_not_allowed">不允許重新分享</string>
|
||||
<string name="resized_image_not_possible_download">沒有可用調整過的圖片。下載完整圖片?</string>
|
||||
|
|
Loading…
Reference in a new issue