mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-25 22:01:16 +00:00
Avoid the same node being added multiple times (fixes #65).
When trying to add a node that already exists, the existing node is edited instead.
This commit is contained in:
parent
6a85fae8ca
commit
64bad6d0fe
9 changed files with 112 additions and 47 deletions
|
@ -11,13 +11,11 @@ import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
@ -31,9 +29,7 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity that allows selecting a directory in the local file system.
|
* Activity that allows selecting a directory in the local file system.
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package com.nutomic.syncthingandroid.gui;
|
package com.nutomic.syncthingandroid.gui;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ActionBarDrawerToggle;
|
import android.support.v4.app.ActionBarDrawerToggle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
@ -15,7 +11,6 @@ import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.nutomic.syncthingandroid.gui;
|
package com.nutomic.syncthingandroid.gui;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
|
@ -34,7 +33,8 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class NodeSettingsActivity extends PreferenceActivity implements
|
public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||||
RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiChangeListener {
|
RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiChangeListener,
|
||||||
|
RestApi.OnNodeIdNormalizedListener {
|
||||||
|
|
||||||
public static final String ACTION_CREATE = "create";
|
public static final String ACTION_CREATE = "create";
|
||||||
|
|
||||||
|
@ -161,15 +161,16 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.create:
|
case R.id.create:
|
||||||
if (mNode.NodeID.equals("")) {
|
if (mNode.NodeID.equals("")) {
|
||||||
Toast.makeText(this, R.string.node_id_required, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.node_id_required, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (mNode.Name.equals("")) {
|
if (mNode.Name.equals("")) {
|
||||||
Toast.makeText(this, R.string.node_name_required, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.node_name_required, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
mSyncthingService.getApi().editNode(mNode, true, this);
|
mSyncthingService.getApi().editNode(mNode, this);
|
||||||
finish();
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.share_node_id:
|
case R.id.share_node_id:
|
||||||
RestApi.shareNodeId(this, mNode.NodeID);
|
RestApi.shareNodeId(this, mNode.NodeID);
|
||||||
|
@ -255,7 +256,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
*/
|
*/
|
||||||
private void nodeUpdated() {
|
private void nodeUpdated() {
|
||||||
if (getIntent().getAction().equals(ACTION_EDIT)) {
|
if (getIntent().getAction().equals(ACTION_EDIT)) {
|
||||||
mSyncthingService.getApi().editNode(mNode, false, this);
|
mSyncthingService.getApi().editNode(mNode, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,4 +288,22 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for {@link RestApi#editNode(RestApi.Node, RestApi.OnNodeIdNormalizedListener)}.
|
||||||
|
* Displays an error message if present, or finishes the Activity on success in edit mode.
|
||||||
|
*
|
||||||
|
* @param normalizedId The normalized node ID, or null on error.
|
||||||
|
* @param error An error message, or null on success.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onNodeIdNormalized(String normalizedId, String error) {
|
||||||
|
if (error != null) {
|
||||||
|
Toast.makeText(NodeSettingsActivity.this, error,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
else if (getIntent().getAction().equals(ACTION_CREATE)) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ package com.nutomic.syncthingandroid.gui;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ListFragment;
|
import android.support.v4.app.ListFragment;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,7 @@ package com.nutomic.syncthingandroid.gui;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ListFragment;
|
import android.support.v4.app.ListFragment;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import android.view.View;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
|
|
@ -37,6 +37,8 @@ public class GetTask extends AsyncTask<String, Void, String> {
|
||||||
|
|
||||||
public static final String URI_MODEL = "/rest/model";
|
public static final String URI_MODEL = "/rest/model";
|
||||||
|
|
||||||
|
public static final String URI_NODEID = "/rest/nodeid";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* params[0] Syncthing hostname
|
* params[0] Syncthing hostname
|
||||||
* params[1] URI to call
|
* params[1] URI to call
|
||||||
|
|
|
@ -647,33 +647,56 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates or creates the given node.
|
* Updates or creates the given node, depending on whether it already exists.
|
||||||
|
*
|
||||||
|
* @param node Settings of the node to edit. To create a node, pass a non-existant node ID.
|
||||||
|
* @param listener {@link OnNodeIdNormalizedListener} for the normalized node ID.
|
||||||
*/
|
*/
|
||||||
public void editNode(Node node, boolean create, Activity activity) {
|
public void editNode(final Node node,
|
||||||
try {
|
final OnNodeIdNormalizedListener listener) {
|
||||||
JSONArray nodes = mConfig.getJSONArray("Nodes");
|
mSyncthingService.getApi().normalizeNodeId(node.NodeID,
|
||||||
JSONObject n = null;
|
new RestApi.OnNodeIdNormalizedListener() {
|
||||||
if (create) {
|
@Override
|
||||||
n = new JSONObject();
|
public void onNodeIdNormalized(String normalizedId, String error) {
|
||||||
nodes.put(n);
|
listener.onNodeIdNormalized(normalizedId, error);
|
||||||
}
|
if (normalizedId == null)
|
||||||
else {
|
return;
|
||||||
for (int i = 0; i < nodes.length(); i++) {
|
|
||||||
JSONObject json = nodes.getJSONObject(i);
|
node.NodeID = normalizedId;
|
||||||
if (node.NodeID.equals(json.getString("NodeID"))) {
|
// If the node already exists, just update it.
|
||||||
n = nodes.getJSONObject(i);
|
boolean create = true;
|
||||||
break;
|
for (RestApi.Node n : getNodes()) {
|
||||||
|
if (n.NodeID.equals(node.NodeID)) {
|
||||||
|
create = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray nodes = mConfig.getJSONArray("Nodes");
|
||||||
|
JSONObject n = null;
|
||||||
|
if (create) {
|
||||||
|
n = new JSONObject();
|
||||||
|
nodes.put(n);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < nodes.length(); i++) {
|
||||||
|
JSONObject json = nodes.getJSONObject(i);
|
||||||
|
if (node.NodeID.equals(json.getString("NodeID"))) {
|
||||||
|
n = nodes.getJSONObject(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.put("NodeID", node.NodeID);
|
||||||
|
n.put("Name", node.Name);
|
||||||
|
n.put("Addresses", listToJson(node.Addresses.split(" ")));
|
||||||
|
configUpdated(mSyncthingService);
|
||||||
|
}
|
||||||
|
catch (JSONException e) {
|
||||||
|
Log.w(TAG, "Failed to read nodes", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
n.put("NodeID", node.NodeID);
|
|
||||||
n.put("Name", node.Name);
|
|
||||||
n.put("Addresses", listToJson(node.Addresses.split(" ")));
|
|
||||||
configUpdated(activity);
|
|
||||||
}
|
|
||||||
catch (JSONException e) {
|
|
||||||
Log.w(TAG, "Failed to read nodes", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -781,6 +804,42 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
return newArray;
|
return newArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result listener for {@link #normalizeNodeId(String, OnNodeIdNormalizedListener)}.
|
||||||
|
*/
|
||||||
|
public interface OnNodeIdNormalizedListener {
|
||||||
|
/**
|
||||||
|
* On any call, exactly one parameter will be null.
|
||||||
|
*
|
||||||
|
* @param normalizedId The normalized node ID, or null on error.
|
||||||
|
* @param error An error message, or null on success.
|
||||||
|
*/
|
||||||
|
public void onNodeIdNormalized(String normalizedId, String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a given node ID.
|
||||||
|
*/
|
||||||
|
public void normalizeNodeId(String id, final OnNodeIdNormalizedListener listener) {
|
||||||
|
new GetTask() {
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String s) {
|
||||||
|
super.onPostExecute(s);
|
||||||
|
String normalized = null;
|
||||||
|
String error = null;
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(s);
|
||||||
|
normalized = json.optString("id", null);
|
||||||
|
error = json.optString("error", null);
|
||||||
|
}
|
||||||
|
catch (JSONException e) {
|
||||||
|
Log.d(TAG, "Failed to parse normalized node ID JSON", e);
|
||||||
|
}
|
||||||
|
listener.onNodeIdNormalized(normalized, error);
|
||||||
|
}
|
||||||
|
}.execute(mUrl, GetTask.URI_NODEID, mApiKey, "id", id);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isApiAvailable() {
|
public boolean isApiAvailable() {
|
||||||
return mAvailableCount.get() == TOTAL_STARTUP_CALLS;
|
return mAvailableCount.get() == TOTAL_STARTUP_CALLS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import java.io.InputStreamReader;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the native syncthing instance and provides an API to access it.
|
* Holds the native syncthing instance and provides an API to access it.
|
||||||
|
|
Loading…
Reference in a new issue