mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-23 11:21:29 +00:00
parent
988bbb0893
commit
b8511cfd41
6 changed files with 98 additions and 20 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue