From 5f99e4379d19e8a9676f0e0f6dc0f57417ca95f6 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 6 Dec 2023 14:02:09 +0100 Subject: [PATCH] Add Two Action Dialog button Signed-off-by: alperozturk --- .../nextcloud/client/di/ComponentsModule.java | 4 + .../com/nextcloud/model/HTTPStatusCodes.kt | 26 +++++ .../ui/activity/ConflictsResolveActivity.kt | 31 ++++- .../ui/activity/UploadListActivity.java | 54 +++++++-- .../android/ui/adapter/UploadListAdapter.java | 14 +-- .../ui/dialog/TwoActionDialogFragment.kt | 110 ++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 7 files changed, 224 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/TwoActionDialogFragment.kt diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 82f9c87381..6a0d4bd5e2 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -104,6 +104,7 @@ import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment; import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; +import com.owncloud.android.ui.dialog.TwoActionDialogFragment; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FeatureFragment; import com.owncloud.android.ui.fragment.FileDetailActivitiesFragment; @@ -269,6 +270,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract ChooseTemplateDialogFragment chooseTemplateDialogFragment(); + @ContributesAndroidInjector + abstract TwoActionDialogFragment twoActionDialogFragment(); + @ContributesAndroidInjector abstract AccountRemovalDialog accountRemovalDialog(); diff --git a/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt new file mode 100644 index 0000000000..2f7e58364d --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2023 Alper Ozturk + * Copyright (C) 2023 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) 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 . + */ + +package com.nextcloud.model + +enum class HTTPStatusCodes(val code: Int) { + NOT_FOUND(404) +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index f734861922..9ed1bb5995 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -20,7 +20,9 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.widget.Toast +import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.nextcloud.client.account.User +import com.nextcloud.model.HTTPStatusCodes import com.nextcloud.utils.extensions.getParcelableArgument import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile @@ -164,7 +166,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener startDialog() } else { Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode) - showErrorAndFinish() + showErrorAndFinish(result.httpCode) } } catch (e: Exception) { Log_OC.e(TAG, "Error when trying to fetch remote file", e) @@ -203,9 +205,30 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener } } - private fun showErrorAndFinish() { - runOnUiThread { Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show() } - finish() + private fun showErrorAndFinish(code: Int? = null) { + val message = parseErrorMessage(code) + runOnUiThread { + if (code != null) { + sendHandleFileExistenceEventBroadcast() + } else { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } + + finish() + } + } + + private fun sendHandleFileExistenceEventBroadcast() { + val intent = Intent(UploadListActivity.HANDLE_FILE_EXISTENCE_RECEIVER) + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + } + + private fun parseErrorMessage(code: Int?): String { + return if (code == HTTPStatusCodes.NOT_FOUND.code) { + getString(R.string.uploader_file_not_found_on_server_message) + } else { + getString(R.string.conflict_dialog_error) + } } /** diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 7ebc62326a..8b84a8838a 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -56,6 +56,7 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.CheckCurrentCredentialsOperation; import com.owncloud.android.ui.adapter.UploadListAdapter; import com.owncloud.android.ui.decoration.MediaGridItemDecoration; +import com.owncloud.android.ui.dialog.TwoActionDialogFragment; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -66,13 +67,13 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; /** - * Activity listing pending, active, and completed uploads. User can delete - * completed uploads from view. Content of this list of coming from - * {@link UploadsStorageManager}. + * Activity listing pending, active, and completed uploads. User can delete completed uploads from view. Content of this + * list of coming from {@link UploadsStorageManager}. */ -public class UploadListActivity extends FileActivity { +public class UploadListActivity extends FileActivity implements TwoActionDialogFragment.TwoActionDialogActionListener { private static final String TAG = UploadListActivity.class.getSimpleName(); + public static final String HANDLE_FILE_EXISTENCE_RECEIVER = "HANDLE_FILE_EXISTENCE_RECEIVER"; private UploadMessagesReceiver uploadMessagesReceiver; @@ -144,6 +145,44 @@ public class UploadListActivity extends FileActivity { setupDrawer(R.id.nav_uploads); setupContent(); + registerHandleFileExistenceDialogReceiver(); + } + + private void registerHandleFileExistenceDialogReceiver() { + IntentFilter filter = new IntentFilter(HANDLE_FILE_EXISTENCE_RECEIVER); + LocalBroadcastManager.getInstance(this).registerReceiver(handleFileExistenceReceiver, filter); + } + + private final BroadcastReceiver handleFileExistenceReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + showHandleFileExistenceDialog(); + } + }; + + private void showHandleFileExistenceDialog() { + TwoActionDialogFragment dialog = TwoActionDialogFragment.Companion.newInstance(R.string.uploader_handle_not_existed_file_dialog_title, + R.string.uploader_handle_not_existed_file_dialog_message, + R.string.common_cancel, + R.string.common_ok, + this); + dialog.show(this.getSupportFragmentManager(), null); + } + + @Override + public void positiveAction() { + + } + + @Override + public void negativeAction() { + + } + + @Override + protected void onDestroy() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(handleFileExistenceReceiver); + super.onDestroy(); } private void setupContent() { @@ -178,6 +217,7 @@ public class UploadListActivity extends FileActivity { swipeListRefreshLayout.setOnRefreshListener(this::refresh); loadItems(); + } private void loadItems() { @@ -327,14 +367,14 @@ public class UploadListActivity extends FileActivity { if (mUploaderBinder == null) { mUploaderBinder = (FileUploaderBinder) service; Log_OC.d(TAG, "UploadListActivity connected to Upload service. component: " + - component + " service: " + service); + component + " service: " + service); } else { Log_OC.d(TAG, "mUploaderBinder already set. mUploaderBinder: " + - mUploaderBinder + " service:" + service); + mUploaderBinder + " service:" + service); } } else { Log_OC.d(TAG, "UploadListActivity not connected to Upload service. component: " + - component + " service: " + service); + component + " service: " + service); } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index ec95717079..1d330c517b 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -503,9 +503,9 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter. + */ + +package com.owncloud.android.ui.dialog + +import android.app.Dialog +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.di.Injectable +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class TwoActionDialogFragment(val listener: TwoActionDialogActionListener) : DialogFragment(), Injectable { + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + interface TwoActionDialogActionListener { + fun positiveAction() + fun negativeAction() + } + + companion object { + const val titleArgument = "titleArgument" + const val messageArgument = "messageArgument" + const val negativeButtonArgument = "negativeButtonArgument" + const val positiveButtonArgument = "positiveButtonArgument" + + fun newInstance( + titleId: Int, + messageId: Int?, + negativeButtonTextId: Int, + positiveButtonTextId: Int, + listener: TwoActionDialogActionListener + ): TwoActionDialogFragment { + return TwoActionDialogFragment(listener).apply { + arguments = Bundle().apply { + putInt(titleArgument, titleId) + messageId?.let { putInt(messageArgument, it) } + putInt(positiveButtonArgument, positiveButtonTextId) + putInt(negativeButtonArgument, negativeButtonTextId) + } + } + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val title = requireContext().getString(requireArguments().getInt(titleArgument)) + val messageId: Int? = arguments?.getInt(messageArgument) + val positiveButtonText = requireContext().getString(requireArguments().getInt(positiveButtonArgument)) + val negativeButtonText = requireContext().getString(requireArguments().getInt(negativeButtonArgument)) + + val builder = MaterialAlertDialogBuilder(requireContext()) + .setTitle(title) + .setNegativeButton(negativeButtonText) { dialog, _ -> + dialog.dismiss() + listener.negativeAction() + } + .setPositiveButton(positiveButtonText) { dialog, _ -> + dialog.dismiss() + listener.positiveAction() + } + + messageId?.let { + builder.setMessage(requireContext().getString(messageId)) + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder) + + return builder.create() + } + + override fun onStart() { + super.onStart() + + val alertDialog = dialog as AlertDialog? + alertDialog?.let { + val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton? + if (positiveButton != null) { + viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton) + } + + val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton? + if (negativeButton != null) { + viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton) + } + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3a5bf57199..5b1987c5e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -912,6 +912,9 @@ Failed to start editor Add folder info creates folder info + File Not Found + This file does not longer exist in server still want to upload? + We couldnt locate the file on server. Another user may have deleted the file File not found. Are you sure this file exist or conflict not solved before? File upload conflict Pick which version to keep of %1$s