From 7a6ec4c8831e6c290f24716a9efe2491979b224c Mon Sep 17 00:00:00 2001 From: ZacharyACoon Date: Sun, 31 Mar 2024 19:53:34 -0700 Subject: [PATCH] greatly improve override_changes ux/ui --- .../activities/DeviceActivity.java | 6 +- .../activities/FolderActivity.java | 62 +++++++++++++++++-- .../fragments/FolderListFragment.java | 8 ++- .../syncthingandroid/model/FolderStatus.java | 8 +++ .../views/FoldersAdapter.java | 19 ++---- .../drawable/outline_arrow_circle_up_24.xml | 10 +++ app/src/main/res/layout/fragment_folder.xml | 55 ++++++++++++++++ app/src/main/res/layout/item_folder_list.xml | 17 +---- app/src/main/res/values/strings.xml | 4 +- 9 files changed, 150 insertions(+), 39 deletions(-) create mode 100644 app/src/main/res/drawable/outline_arrow_circle_up_24.xml diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/DeviceActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/DeviceActivity.java index 2aabb36c..0b67bc74 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/DeviceActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/DeviceActivity.java @@ -57,7 +57,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis private static final String TAG = "DeviceSettingsFragment"; private static final String IS_SHOWING_DISCARD_DIALOG = "DISCARD_FOLDER_DIALOG_STATE"; private static final String IS_SHOWING_COMPRESSION_DIALOG = "COMPRESSION_FOLDER_DIALOG_STATE"; - private static final String IS_SHOWING_DELETE_DIALOG = "DELETE_FOLDER_DIALOG_STATE"; + private static final String IS_SHOW_DELETE_DIALOG = "DELETE_FOLDER_DIALOG_STATE"; private static final int QR_SCAN_REQUEST_CODE = 777; private static final List DYNAMIC_ADDRESS = Collections.singletonList("dynamic"); @@ -170,7 +170,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis showCompressionDialog(); } - if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ + if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){ showDeleteDialog(); } @@ -220,7 +220,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis outState.putBoolean(IS_SHOWING_COMPRESSION_DIALOG, mCompressionDialog != null && mCompressionDialog.isShowing()); Util.dismissDialogSafe(mCompressionDialog, this); - outState.putBoolean(IS_SHOWING_DELETE_DIALOG, mDeleteDialog != null && mDeleteDialog.isShowing()); + outState.putBoolean(IS_SHOW_DELETE_DIALOG, mDeleteDialog != null && mDeleteDialog.isShowing()); Util.dismissDialogSafe(mDeleteDialog, this); } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/FolderActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/FolderActivity.java index 3210ac35..1d3a13e1 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/FolderActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/FolderActivity.java @@ -30,6 +30,7 @@ import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.databinding.FragmentFolderBinding; import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Folder; +import com.nutomic.syncthingandroid.model.FolderStatus; import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; @@ -65,13 +66,16 @@ public class FolderActivity extends SyncthingActivity "com.nutomic.syncthingandroid.activities.FolderActivity.FOLDER_ID"; public static final String EXTRA_FOLDER_LABEL = "com.nutomic.syncthingandroid.activities.FolderActivity.FOLDER_LABEL"; + public static final String EXTRA_FOLDER_OVERRIDABLE_CHANGES = + "com.nutomic.syncthingandroid.activities.FolderActivity.FOLDER_OVERRIDABLE_CHANGES"; public static final String EXTRA_DEVICE_ID = "com.nutomic.syncthingandroid.activities.FolderActivity.DEVICE_ID"; private static final String TAG = "FolderActivity"; - private static final String IS_SHOWING_DELETE_DIALOG = "DELETE_FOLDER_DIALOG_STATE"; + private static final String IS_SHOW_DELETE_DIALOG = "DELETE_FOLDER_DIALOG_STATE"; private static final String IS_SHOW_DISCARD_DIALOG = "DISCARD_FOLDER_DIALOG_STATE"; + private static final String IS_SHOW_OVERRIDE_CHANGES_DIALOG = "OVERRIDE_REMOTES_DIALOG_STATE"; private static final int FILE_VERSIONING_DIALOG_REQUEST = 3454; private static final int PULL_ORDER_DIALOG_REQUEST = 3455; @@ -93,6 +97,7 @@ public class FolderActivity extends SyncthingActivity private Dialog mDeleteDialog; private Dialog mDiscardDialog; + private Dialog mOverrideChangesDialog; private Folder.Versioning mVersioning; @@ -148,6 +153,7 @@ public class FolderActivity extends SyncthingActivity findViewById(R.id.pullOrderContainer).setOnClickListener(v -> showPullOrderDialog()); findViewById(R.id.versioningContainer).setOnClickListener(v -> showVersioningDialog()); binding.editIgnores.setOnClickListener(v -> editIgnores()); + binding.overrideChangesContainer.setOnClickListener(v -> showOverrideChangesDialog()); if (mIsCreateMode) { if (savedInstanceState != null) { @@ -162,6 +168,7 @@ public class FolderActivity extends SyncthingActivity // Open keyboard on label view in edit mode. binding.label.requestFocus(); binding.editIgnores.setEnabled(false); + setOverrideChangesContainerEnabled(false); } else { // Prepare edit mode. @@ -169,19 +176,31 @@ public class FolderActivity extends SyncthingActivity binding.id.setFocusable(false); binding.id.setEnabled(false); binding.directoryTextView.setEnabled(false); + + // overridable remotes button + setOverrideChangesContainerEnabled( + getIntent().getBooleanExtra(EXTRA_FOLDER_OVERRIDABLE_CHANGES, false) + ); } if (savedInstanceState != null){ - if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ + if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){ showDeleteDialog(); } } if (savedInstanceState != null){ - if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ + if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){ showDeleteDialog(); } } + + if (savedInstanceState != null) { + if (savedInstanceState.getBoolean(IS_SHOW_OVERRIDE_CHANGES_DIALOG)){ + showOverrideChangesDialog(); + } + } + } /** @@ -304,12 +323,18 @@ public class FolderActivity extends SyncthingActivity @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean(IS_SHOWING_DELETE_DIALOG, mDeleteDialog != null && mDeleteDialog.isShowing()); + outState.putBoolean(IS_SHOW_DELETE_DIALOG, mDeleteDialog != null && mDeleteDialog.isShowing()); Util.dismissDialogSafe(mDeleteDialog, this); + outState.putBoolean(IS_SHOW_OVERRIDE_CHANGES_DIALOG, mOverrideChangesDialog != null && mOverrideChangesDialog.isShowing()); + Util.dismissDialogSafe(mOverrideChangesDialog, this); + if (mIsCreateMode){ outState.putBoolean(IS_SHOW_DISCARD_DIALOG, mDiscardDialog != null && mDiscardDialog.isShowing()); Util.dismissDialogSafe(mDiscardDialog, this); + + outState.putBoolean(IS_SHOW_OVERRIDE_CHANGES_DIALOG, mOverrideChangesDialog != null && mOverrideChangesDialog.isShowing()); + Util.dismissDialogSafe(mOverrideChangesDialog, this); } } @@ -778,4 +803,33 @@ public class FolderActivity extends SyncthingActivity binding.versioningType.setText(type); binding.versioningDescription.setText(description); } + + private void setOverrideChangesContainerEnabled(boolean state) { + binding.overrideChangesContainer.setEnabled(state); + for ( int i = 0; i < binding.overrideChangesContainer.getChildCount(); i++ ) { + binding.overrideChangesContainer.getChildAt(i).setEnabled(state); + } + } + + private void showOverrideChangesDialog(){ + mOverrideChangesDialog = createOverrideChangesDialog(); + mOverrideChangesDialog.show(); + } + + private Dialog createOverrideChangesDialog(){ + return Util.getAlertDialogBuilder(this) + .setIcon(R.drawable.outline_arrow_circle_up_24) + .setTitle(R.string.override_changes) + .setMessage(R.string.override_changes_are_you_sure) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> { + RestApi restApi = getApi(); + if (restApi != null) { + restApi.overrideChanges(mFolder.id); + mFolderNeedsToUpdate = true; + } + finish(); + }) + .create(); + } } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java b/app/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java index 8da44b77..f0737d1c 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java @@ -13,6 +13,7 @@ import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.activities.FolderActivity; import com.nutomic.syncthingandroid.activities.SyncthingActivity; import com.nutomic.syncthingandroid.model.Folder; +import com.nutomic.syncthingandroid.model.FolderStatus; import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; @@ -101,9 +102,14 @@ public class FolderListFragment extends ListFragment implements SyncthingService @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { + Folder folder = mAdapter.getItem(i); + FolderStatus folderStatus = mAdapter.getLocalFolderStatus(folder.id); + boolean overridableChanges = folder.type.equals(Constants.FOLDER_TYPE_SEND_ONLY) + && folderStatus.isOutOfSync(); Intent intent = new Intent(getActivity(), FolderActivity.class) .putExtra(FolderActivity.EXTRA_IS_CREATE, false) - .putExtra(FolderActivity.EXTRA_FOLDER_ID, mAdapter.getItem(i).id); + .putExtra(FolderActivity.EXTRA_FOLDER_ID, mAdapter.getItem(i).id) + .putExtra(FolderActivity.EXTRA_FOLDER_OVERRIDABLE_CHANGES, overridableChanges); startActivity(intent); } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/model/FolderStatus.java b/app/src/main/java/com/nutomic/syncthingandroid/model/FolderStatus.java index 2f5f4d62..c3df2976 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/model/FolderStatus.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/model/FolderStatus.java @@ -27,4 +27,12 @@ public class FolderStatus { public long version; public String error; public String watchError; + + public boolean isNeedsItems() { + return needFiles + needDirectories + needSymlinks + needDeletes > 0; + } + + public boolean isOutOfSync() { + return state.equals("idle") && isNeedsItems(); + } } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/views/FoldersAdapter.java b/app/src/main/java/com/nutomic/syncthingandroid/views/FoldersAdapter.java index 4605f7d9..4c2b680b 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/views/FoldersAdapter.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/views/FoldersAdapter.java @@ -59,13 +59,6 @@ public class FoldersAdapter extends ArrayAdapter { Folder folder = getItem(position); binding.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label); binding.directory.setText(folder.path); - binding.override.setOnClickListener(v -> { - // Send "Override changes" through our service to the REST API. - Intent intent = new Intent(mContext, SyncthingService.class) - .putExtra(SyncthingService.EXTRA_FOLDER_ID, folder.id); - intent.setAction(SyncthingService.ACTION_OVERRIDE_CHANGES); - mContext.startService(intent); - }); binding.openFolder.setOnClickListener(v -> { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(new File(folder.path)), "resource/folder"); @@ -94,17 +87,12 @@ public class FoldersAdapter extends ArrayAdapter { FolderStatus folderStatus = mLocalFolderStatuses.get(folder.id); if (folderStatus == null) { binding.items.setVisibility(GONE); - binding.override.setVisibility(GONE); binding.size.setVisibility(GONE); setTextOrHide(binding.invalid, folder.invalid); return; } - long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes; - boolean outOfSync = folderStatus.state.equals("idle") && neededItems > 0; - boolean overrideButtonVisible = folder.type.equals(Constants.FOLDER_TYPE_SEND_ONLY) && outOfSync; - binding.override.setVisibility(overrideButtonVisible ? VISIBLE : GONE); - if (outOfSync) { + if (folderStatus.isOutOfSync()) { binding.state.setText(mContext.getString(R.string.status_outofsync)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red)); } else { @@ -172,6 +160,10 @@ public class FoldersAdapter extends ArrayAdapter { } } + public FolderStatus getLocalFolderStatus(String folderId) { + return mLocalFolderStatuses.get(folderId); + } + private void onReceiveFolderStatus(String folderId, FolderStatus folderStatus) { mLocalFolderStatuses.put(folderId, folderStatus); notifyDataSetChanged(); @@ -185,5 +177,4 @@ public class FoldersAdapter extends ArrayAdapter { view.setVisibility(VISIBLE); } } - } diff --git a/app/src/main/res/drawable/outline_arrow_circle_up_24.xml b/app/src/main/res/drawable/outline_arrow_circle_up_24.xml new file mode 100644 index 00000000..d37ba61d --- /dev/null +++ b/app/src/main/res/drawable/outline_arrow_circle_up_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_folder.xml b/app/src/main/res/layout/fragment_folder.xml index 963a7c04..6cb3622b 100644 --- a/app/src/main/res/layout/fragment_folder.xml +++ b/app/src/main/res/layout/fragment_folder.xml @@ -217,6 +217,16 @@ android:textAppearance="@style/TextAppearance.AppCompat.Caption" android:layout_marginBottom="10dp" /> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_folder_list.xml b/app/src/main/res/layout/item_folder_list.xml index e5fad950..0965de2a 100644 --- a/app/src/main/res/layout/item_folder_list.xml +++ b/app/src/main/res/layout/item_folder_list.xml @@ -44,26 +44,11 @@ android:ellipsize="end" android:textAppearance="?textAppearanceListItemSecondary" /> -