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

Add "Override changes" UI button (fixes #396) (#1183)

This commit is contained in:
Catfriend1 2018-07-18 08:20:26 +02:00 committed by Audrius Butkevicius
parent 988bbb0893
commit b8511cfd41
6 changed files with 98 additions and 20 deletions

View file

@ -0,0 +1,26 @@
package com.nutomic.syncthingandroid.http;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.android.volley.Request;
import com.google.common.base.Optional;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
public class PostRequest extends ApiRequest {
public static final String URI_DB_OVERRIDE = "/rest/db/override";
public PostRequest(Context context, URL url, String path, String apiKey,
@Nullable Map<String, String> params, OnSuccessListener listener) {
super(context, url, path, apiKey);
Map<String, String> safeParams = Optional.fromNullable(params).or(Collections.emptyMap());
Uri uri = buildUri(safeParams);
connect(Request.Method.POST, uri, null, listener, null);
}
}

View file

@ -20,6 +20,7 @@ import com.nutomic.syncthingandroid.BuildConfig;
import com.nutomic.syncthingandroid.SyncthingApp; import com.nutomic.syncthingandroid.SyncthingApp;
import com.nutomic.syncthingandroid.activities.ShareActivity; import com.nutomic.syncthingandroid.activities.ShareActivity;
import com.nutomic.syncthingandroid.http.GetRequest; import com.nutomic.syncthingandroid.http.GetRequest;
import com.nutomic.syncthingandroid.http.PostRequest;
import com.nutomic.syncthingandroid.http.PostConfigRequest; import com.nutomic.syncthingandroid.http.PostConfigRequest;
import com.nutomic.syncthingandroid.model.Config; import com.nutomic.syncthingandroid.model.Config;
import com.nutomic.syncthingandroid.model.Completion; import com.nutomic.syncthingandroid.model.Completion;
@ -268,6 +269,16 @@ public class RestApi {
} }
} }
/**
* Override folder changes. This is the same as hitting
* the "override changes" button from the web UI.
*/
public void overrideChanges(String folderId) {
Log.d(TAG, "overrideChanges '" + folderId + "'");
new PostRequest(mContext, mUrl, PostRequest.URI_DB_OVERRIDE, mApiKey,
ImmutableMap.of("folder", folderId), null);
}
/** /**
* Sends current config to Syncthing. * Sends current config to Syncthing.
* Will result in a "ConfigSaved" event. * Will result in a "ConfigSaved" event.

View file

@ -71,6 +71,12 @@ public class SyncthingService extends Service {
public static final String ACTION_IGNORE_FOLDER = public static final String ACTION_IGNORE_FOLDER =
"com.nutomic.syncthingandroid.service.SyncthingService.IGNORE_FOLDER"; "com.nutomic.syncthingandroid.service.SyncthingService.IGNORE_FOLDER";
/**
* Intent action to override folder changes.
*/
public static final String ACTION_OVERRIDE_CHANGES =
"com.nutomic.syncthingandroid.service.SyncthingService.OVERRIDE_CHANGES";
/** /**
* Extra used together with ACTION_IGNORE_DEVICE, ACTION_IGNORE_FOLDER. * Extra used together with ACTION_IGNORE_DEVICE, ACTION_IGNORE_FOLDER.
*/ */
@ -242,6 +248,8 @@ public class SyncthingService extends Service {
// mApi is not null due to State.ACTIVE // mApi is not null due to State.ACTIVE
mApi.ignoreFolder(intent.getStringExtra(EXTRA_FOLDER_ID)); mApi.ignoreFolder(intent.getStringExtra(EXTRA_FOLDER_ID));
mNotificationHandler.cancelConsentNotification(intent.getIntExtra(EXTRA_NOTIFICATION_ID, 0)); mNotificationHandler.cancelConsentNotification(intent.getIntExtra(EXTRA_NOTIFICATION_ID, 0));
} else if (ACTION_OVERRIDE_CHANGES.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
mApi.overrideChanges(intent.getStringExtra(EXTRA_FOLDER_ID));
} }
return START_STICKY; return START_STICKY;
} }

View file

@ -19,7 +19,9 @@ import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.databinding.ItemFolderListBinding; import com.nutomic.syncthingandroid.databinding.ItemFolderListBinding;
import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.model.FolderStatus; import com.nutomic.syncthingandroid.model.FolderStatus;
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.util.Util; import com.nutomic.syncthingandroid.util.Util;
import java.io.File; import java.io.File;
@ -37,36 +39,46 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
private final HashMap<String, FolderStatus> mLocalFolderStatuses = new HashMap<>(); private final HashMap<String, FolderStatus> mLocalFolderStatuses = new HashMap<>();
private final Context mContext;
public FoldersAdapter(Context context) { public FoldersAdapter(Context context) {
super(context, 0); super(context, 0);
mContext = context;
} }
@Override @Override
@NonNull @NonNull
public View getView(int position, View convertView, @NonNull ViewGroup parent) { public View getView(int position, View convertView, @NonNull ViewGroup parent) {
ItemFolderListBinding binding = (convertView == null) ItemFolderListBinding binding = (convertView == null)
? DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.item_folder_list, parent, false) ? DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.item_folder_list, parent, false)
: DataBindingUtil.bind(convertView); : DataBindingUtil.bind(convertView);
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");
intent.putExtra("org.openintents.extra.ABSOLUTE_PATH", folder.path); intent.putExtra("org.openintents.extra.ABSOLUTE_PATH", folder.path);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(getContext().getPackageManager()) != null) { if (intent.resolveActivity(mContext.getPackageManager()) != null) {
getContext().startActivity(intent); mContext.startActivity(intent);
} else { } else {
// Try a second way to find a compatible file explorer app. // Try a second way to find a compatible file explorer app.
Log.v(TAG, "openFolder: Fallback to application chooser to open folder."); Log.v(TAG, "openFolder: Fallback to application chooser to open folder.");
intent.setDataAndType(Uri.parse(folder.path), "application/*"); intent.setDataAndType(Uri.parse(folder.path), "application/*");
Intent chooserIntent = Intent.createChooser(intent, getContext().getString(R.string.open_file_manager)); Intent chooserIntent = Intent.createChooser(intent, mContext.getString(R.string.open_file_manager));
if (chooserIntent != null) { if (chooserIntent != null) {
getContext().startActivity(chooserIntent); mContext.startActivity(chooserIntent);
} else { } else {
Toast.makeText(getContext(), R.string.toast_no_file_manager, Toast.LENGTH_SHORT).show(); Toast.makeText(mContext, R.string.toast_no_file_manager, Toast.LENGTH_SHORT).show();
} }
} }
}); });
@ -79,6 +91,7 @@ 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;
@ -88,35 +101,38 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
? Math.round(100 * folderStatus.inSyncBytes / folderStatus.globalBytes) ? Math.round(100 * folderStatus.inSyncBytes / folderStatus.globalBytes)
: 100; : 100;
long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes; long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes;
if (folderStatus.state.equals("idle") && neededItems > 0) { boolean outOfSync = folderStatus.state.equals("idle") && neededItems > 0;
binding.state.setText(getContext().getString(R.string.status_outofsync)); boolean overrideButtonVisible = (folder.type == Constants.FOLDER_TYPE_SEND_ONLY) && outOfSync;
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red)); binding.override.setVisibility(overrideButtonVisible ? VISIBLE : GONE);
if (outOfSync) {
binding.state.setText(mContext.getString(R.string.status_outofsync));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
} else { } else {
if (folder.paused) { if (folder.paused) {
binding.state.setText(getContext().getString(R.string.state_paused)); binding.state.setText(mContext.getString(R.string.state_paused));
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_black)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_black));
} else { } else {
binding.state.setText(getLocalizedState(getContext(), folderStatus.state, percentage)); binding.state.setText(getLocalizedState(mContext, folderStatus.state, percentage));
switch(folderStatus.state) { switch(folderStatus.state) {
case "idle": case "idle":
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_green));
break; break;
case "scanning": case "scanning":
case "syncing": case "syncing":
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
break; break;
default: default:
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red)); binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
} }
} }
} }
binding.items.setVisibility(VISIBLE); binding.items.setVisibility(VISIBLE);
binding.items.setText(getContext().getResources() binding.items.setText(mContext.getResources()
.getQuantityString(R.plurals.files, (int) folderStatus.inSyncFiles, folderStatus.inSyncFiles, folderStatus.globalFiles)); .getQuantityString(R.plurals.files, (int) folderStatus.inSyncFiles, folderStatus.inSyncFiles, folderStatus.globalFiles));
binding.size.setVisibility(VISIBLE); binding.size.setVisibility(VISIBLE);
binding.size.setText(getContext().getString(R.string.folder_size_format, binding.size.setText(mContext.getString(R.string.folder_size_format,
Util.readableFileSize(getContext(), folderStatus.inSyncBytes), Util.readableFileSize(mContext, folderStatus.inSyncBytes),
Util.readableFileSize(getContext(), folderStatus.globalBytes))); Util.readableFileSize(mContext, folderStatus.globalBytes)));
setTextOrHide(binding.invalid, folderStatus.invalid); setTextOrHide(binding.invalid, folderStatus.invalid);
} }

View file

@ -45,11 +45,26 @@
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/directory" android:layout_below="@id/override"
android:textAppearance="?textAppearanceListItemSecondary" /> android:textAppearance="?textAppearanceListItemSecondary" />
<TextView <TextView

View file

@ -86,6 +86,8 @@ Please report any problems you encounter via Github.</string>
<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="open_file_manager">Open file manager</string> <string name="open_file_manager">Open file manager</string>
<string name="toast_no_file_manager">No compatible file manager found</string> <string name="toast_no_file_manager">No compatible file manager found</string>