Merge pull request #12172 from nextcloud/refactor/convert-ConflictsResolveActivity-to-kt

Convert ConflictsResolveActivity to Kotlin
This commit is contained in:
Tobias Kaminsky 2023-12-04 08:38:29 +01:00 committed by GitHub
commit 11973d6bc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 249 additions and 257 deletions

View file

@ -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;
}
}

View file

@ -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
}
}
}