1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-12-23 03:11:30 +00:00

greatly improve override_changes ux/ui

This commit is contained in:
ZacharyACoon 2024-03-31 19:53:34 -07:00
parent d415716256
commit 7a6ec4c883
9 changed files with 150 additions and 39 deletions

View file

@ -57,7 +57,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
private static final String TAG = "DeviceSettingsFragment"; private static final String TAG = "DeviceSettingsFragment";
private static final String IS_SHOWING_DISCARD_DIALOG = "DISCARD_FOLDER_DIALOG_STATE"; 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_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 int QR_SCAN_REQUEST_CODE = 777;
private static final List<String> DYNAMIC_ADDRESS = Collections.singletonList("dynamic"); private static final List<String> DYNAMIC_ADDRESS = Collections.singletonList("dynamic");
@ -170,7 +170,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
showCompressionDialog(); showCompressionDialog();
} }
if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){
showDeleteDialog(); showDeleteDialog();
} }
@ -220,7 +220,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
outState.putBoolean(IS_SHOWING_COMPRESSION_DIALOG, mCompressionDialog != null && mCompressionDialog.isShowing()); outState.putBoolean(IS_SHOWING_COMPRESSION_DIALOG, mCompressionDialog != null && mCompressionDialog.isShowing());
Util.dismissDialogSafe(mCompressionDialog, this); 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); Util.dismissDialogSafe(mDeleteDialog, this);
} }

View file

@ -30,6 +30,7 @@ import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.databinding.FragmentFolderBinding; import com.nutomic.syncthingandroid.databinding.FragmentFolderBinding;
import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.model.FolderStatus;
import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.Constants;
import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.RestApi;
import com.nutomic.syncthingandroid.service.SyncthingService; import com.nutomic.syncthingandroid.service.SyncthingService;
@ -65,13 +66,16 @@ public class FolderActivity extends SyncthingActivity
"com.nutomic.syncthingandroid.activities.FolderActivity.FOLDER_ID"; "com.nutomic.syncthingandroid.activities.FolderActivity.FOLDER_ID";
public static final String EXTRA_FOLDER_LABEL = public static final String EXTRA_FOLDER_LABEL =
"com.nutomic.syncthingandroid.activities.FolderActivity.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 = public static final String EXTRA_DEVICE_ID =
"com.nutomic.syncthingandroid.activities.FolderActivity.DEVICE_ID"; "com.nutomic.syncthingandroid.activities.FolderActivity.DEVICE_ID";
private static final String TAG = "FolderActivity"; 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_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 FILE_VERSIONING_DIALOG_REQUEST = 3454;
private static final int PULL_ORDER_DIALOG_REQUEST = 3455; private static final int PULL_ORDER_DIALOG_REQUEST = 3455;
@ -93,6 +97,7 @@ public class FolderActivity extends SyncthingActivity
private Dialog mDeleteDialog; private Dialog mDeleteDialog;
private Dialog mDiscardDialog; private Dialog mDiscardDialog;
private Dialog mOverrideChangesDialog;
private Folder.Versioning mVersioning; private Folder.Versioning mVersioning;
@ -148,6 +153,7 @@ public class FolderActivity extends SyncthingActivity
findViewById(R.id.pullOrderContainer).setOnClickListener(v -> showPullOrderDialog()); findViewById(R.id.pullOrderContainer).setOnClickListener(v -> showPullOrderDialog());
findViewById(R.id.versioningContainer).setOnClickListener(v -> showVersioningDialog()); findViewById(R.id.versioningContainer).setOnClickListener(v -> showVersioningDialog());
binding.editIgnores.setOnClickListener(v -> editIgnores()); binding.editIgnores.setOnClickListener(v -> editIgnores());
binding.overrideChangesContainer.setOnClickListener(v -> showOverrideChangesDialog());
if (mIsCreateMode) { if (mIsCreateMode) {
if (savedInstanceState != null) { if (savedInstanceState != null) {
@ -162,6 +168,7 @@ public class FolderActivity extends SyncthingActivity
// Open keyboard on label view in edit mode. // Open keyboard on label view in edit mode.
binding.label.requestFocus(); binding.label.requestFocus();
binding.editIgnores.setEnabled(false); binding.editIgnores.setEnabled(false);
setOverrideChangesContainerEnabled(false);
} }
else { else {
// Prepare edit mode. // Prepare edit mode.
@ -169,19 +176,31 @@ public class FolderActivity extends SyncthingActivity
binding.id.setFocusable(false); binding.id.setFocusable(false);
binding.id.setEnabled(false); binding.id.setEnabled(false);
binding.directoryTextView.setEnabled(false); binding.directoryTextView.setEnabled(false);
// overridable remotes button
setOverrideChangesContainerEnabled(
getIntent().getBooleanExtra(EXTRA_FOLDER_OVERRIDABLE_CHANGES, false)
);
} }
if (savedInstanceState != null){ if (savedInstanceState != null){
if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){
showDeleteDialog(); showDeleteDialog();
} }
} }
if (savedInstanceState != null){ if (savedInstanceState != null){
if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){ if (savedInstanceState.getBoolean(IS_SHOW_DELETE_DIALOG)){
showDeleteDialog(); showDeleteDialog();
} }
} }
if (savedInstanceState != null) {
if (savedInstanceState.getBoolean(IS_SHOW_OVERRIDE_CHANGES_DIALOG)){
showOverrideChangesDialog();
}
}
} }
/** /**
@ -304,12 +323,18 @@ public class FolderActivity extends SyncthingActivity
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(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); Util.dismissDialogSafe(mDeleteDialog, this);
outState.putBoolean(IS_SHOW_OVERRIDE_CHANGES_DIALOG, mOverrideChangesDialog != null && mOverrideChangesDialog.isShowing());
Util.dismissDialogSafe(mOverrideChangesDialog, this);
if (mIsCreateMode){ if (mIsCreateMode){
outState.putBoolean(IS_SHOW_DISCARD_DIALOG, mDiscardDialog != null && mDiscardDialog.isShowing()); outState.putBoolean(IS_SHOW_DISCARD_DIALOG, mDiscardDialog != null && mDiscardDialog.isShowing());
Util.dismissDialogSafe(mDiscardDialog, this); 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.versioningType.setText(type);
binding.versioningDescription.setText(description); 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();
}
} }

View file

@ -13,6 +13,7 @@ import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.FolderActivity; import com.nutomic.syncthingandroid.activities.FolderActivity;
import com.nutomic.syncthingandroid.activities.SyncthingActivity; import com.nutomic.syncthingandroid.activities.SyncthingActivity;
import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.model.FolderStatus;
import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.Constants;
import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.RestApi;
import com.nutomic.syncthingandroid.service.SyncthingService; import com.nutomic.syncthingandroid.service.SyncthingService;
@ -101,9 +102,14 @@ public class FolderListFragment extends ListFragment implements SyncthingService
@Override @Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { 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) Intent intent = new Intent(getActivity(), FolderActivity.class)
.putExtra(FolderActivity.EXTRA_IS_CREATE, false) .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); startActivity(intent);
} }

View file

@ -27,4 +27,12 @@ public class FolderStatus {
public long version; public long version;
public String error; public String error;
public String watchError; public String watchError;
public boolean isNeedsItems() {
return needFiles + needDirectories + needSymlinks + needDeletes > 0;
}
public boolean isOutOfSync() {
return state.equals("idle") && isNeedsItems();
}
} }

View file

@ -59,13 +59,6 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
Folder folder = getItem(position); Folder folder = getItem(position);
binding.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label); binding.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label);
binding.directory.setText(folder.path); 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 -> { binding.openFolder.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(folder.path)), "resource/folder"); intent.setDataAndType(Uri.fromFile(new File(folder.path)), "resource/folder");
@ -94,17 +87,12 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
FolderStatus folderStatus = mLocalFolderStatuses.get(folder.id); FolderStatus folderStatus = mLocalFolderStatuses.get(folder.id);
if (folderStatus == null) { if (folderStatus == null) {
binding.items.setVisibility(GONE); binding.items.setVisibility(GONE);
binding.override.setVisibility(GONE);
binding.size.setVisibility(GONE); binding.size.setVisibility(GONE);
setTextOrHide(binding.invalid, folder.invalid); setTextOrHide(binding.invalid, folder.invalid);
return; return;
} }
long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes; if (folderStatus.isOutOfSync()) {
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) {
binding.state.setText(mContext.getString(R.string.status_outofsync)); binding.state.setText(mContext.getString(R.string.status_outofsync));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
} else { } else {
@ -172,6 +160,10 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
} }
} }
public FolderStatus getLocalFolderStatus(String folderId) {
return mLocalFolderStatuses.get(folderId);
}
private void onReceiveFolderStatus(String folderId, FolderStatus folderStatus) { private void onReceiveFolderStatus(String folderId, FolderStatus folderStatus) {
mLocalFolderStatuses.put(folderId, folderStatus); mLocalFolderStatuses.put(folderId, folderStatus);
notifyDataSetChanged(); notifyDataSetChanged();
@ -185,5 +177,4 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
view.setVisibility(VISIBLE); view.setVisibility(VISIBLE);
} }
} }
} }

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8s8,3.59 8,8S16.41,20 12,20M12,22c5.52,0 10,-4.48 10,-10c0,-5.52 -4.48,-10 -10,-10C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22L12,22zM11,12l0,4h2l0,-4h3l-4,-4l-4,4H11z"/>
</vector>

View file

@ -217,6 +217,16 @@
android:textAppearance="@style/TextAppearance.AppCompat.Caption" android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:layout_marginBottom="10dp" /> android:layout_marginBottom="10dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ignoresContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:gravity="center_vertical">
<TextView <TextView
android:id="@+id/edit_ignores" android:id="@+id/edit_ignores"
style="@style/Widget.Syncthing.TextView.Label.Details" style="@style/Widget.Syncthing.TextView.Label.Details"
@ -229,6 +239,51 @@
android:text="@string/ignore_patterns"/> android:text="@string/ignore_patterns"/>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/actionsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:gravity="center_vertical">
<LinearLayout
android:id="@+id/overrideChangesContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:gravity="center_vertical"
android:enabled="false">
<TextView
android:id="@+id/overrideChanges"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@null"
android:checked="false"
android:drawableLeft="@drawable/outline_arrow_circle_up_24"
android:drawableStart="@drawable/outline_arrow_circle_up_24"
android:text="@string/override_changes"
android:enabled="false"
/>
<TextView
android:id="@+id/overrideChangesDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:layout_marginTop="-10dp"
android:paddingBottom="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:text="@string/override_changes_description"
android:enabled="false"/>
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View file

@ -44,26 +44,11 @@
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?textAppearanceListItemSecondary" /> android:textAppearance="?textAppearanceListItemSecondary" />
<Button
android:id="@+id/override"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/directory"
android:contentDescription="@string/override_changes"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:text="@string/override_changes"
android:drawableLeft="@android:drawable/ic_menu_upload"
android:drawableStart="@android:drawable/ic_menu_upload"
android:textSize="12sp" />
<TextView <TextView
android:id="@+id/items" android:id="@+id/items"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/override" android:layout_below="@id/directory"
android:textAppearance="?textAppearanceListItemSecondary" /> android:textAppearance="?textAppearanceListItemSecondary" />
<TextView <TextView

View file

@ -116,7 +116,9 @@
<item quantity="other">%1$d / %2$d Files</item> <item quantity="other">%1$d / %2$d Files</item>
</plurals> </plurals>
<string name="override_changes">Override changes</string> <string name="override_changes">Override Changes</string>
<string name="override_changes_description">When configured as \"Send Only\" and changes have been made remotely, you may click here to overwrite the folder content on other devices.</string>
<string name="override_changes_are_you_sure"><b>Warning!</b>\n\nThe folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.\n\nAre you sure you want to continue?</string>
<string name="open_file_manager">Open file manager</string> <string name="open_file_manager">Open file manager</string>