From 64bad6d0fe00be05b5d6605ff1b6dd35ff697980 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sun, 17 Aug 2014 22:26:20 +0200 Subject: [PATCH] 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. --- .../gui/FolderPickerActivity.java | 4 - .../gui/LocalNodeInfoFragment.java | 5 - .../gui/NodeSettingsActivity.java | 33 ++++-- .../syncthingandroid/gui/NodesFragment.java | 2 - .../syncthingandroid/gui/ReposFragment.java | 2 - .../syncthingandroid/gui/WebGuiActivity.java | 1 - .../syncthingandroid/syncthing/GetTask.java | 2 + .../syncthingandroid/syncthing/RestApi.java | 109 ++++++++++++++---- .../syncthing/SyncthingService.java | 1 - 9 files changed, 112 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/FolderPickerActivity.java b/src/main/java/com/nutomic/syncthingandroid/gui/FolderPickerActivity.java index 802852e1..d747ecb8 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/FolderPickerActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/FolderPickerActivity.java @@ -11,13 +11,11 @@ import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.support.v7.app.ActionBarActivity; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -31,9 +29,7 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder; import java.io.File; import java.io.FileFilter; -import java.io.IOException; import java.util.Arrays; -import java.util.Stack; /** * Activity that allows selecting a directory in the local file system. diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/LocalNodeInfoFragment.java b/src/main/java/com/nutomic/syncthingandroid/gui/LocalNodeInfoFragment.java index d0727a08..b010aded 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/LocalNodeInfoFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/LocalNodeInfoFragment.java @@ -1,10 +1,6 @@ package com.nutomic.syncthingandroid.gui; -import android.annotation.TargetApi; import android.app.Activity; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; import android.os.Bundle; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; @@ -15,7 +11,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import android.widget.Toast; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.syncthing.RestApi; diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/NodeSettingsActivity.java b/src/main/java/com/nutomic/syncthingandroid/gui/NodeSettingsActivity.java index 77ef2683..34202047 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/NodeSettingsActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/NodeSettingsActivity.java @@ -1,7 +1,6 @@ package com.nutomic.syncthingandroid.gui; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -34,7 +33,8 @@ import java.util.Map; */ public class NodeSettingsActivity extends PreferenceActivity implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener, - RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiChangeListener { + RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiChangeListener, + RestApi.OnNodeIdNormalizedListener { public static final String ACTION_CREATE = "create"; @@ -161,15 +161,16 @@ public class NodeSettingsActivity extends PreferenceActivity implements switch (item.getItemId()) { case R.id.create: 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; } 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; } - mSyncthingService.getApi().editNode(mNode, true, this); - finish(); + mSyncthingService.getApi().editNode(mNode, this); return true; case R.id.share_node_id: RestApi.shareNodeId(this, mNode.NodeID); @@ -255,7 +256,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements */ private void nodeUpdated() { 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(); + } + } + } diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/NodesFragment.java b/src/main/java/com/nutomic/syncthingandroid/gui/NodesFragment.java index 5cc50be8..3e5166ed 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/NodesFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/NodesFragment.java @@ -3,9 +3,7 @@ package com.nutomic.syncthingandroid.gui; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/ReposFragment.java b/src/main/java/com/nutomic/syncthingandroid/gui/ReposFragment.java index 93a0c4c1..c749e7a2 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/ReposFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/ReposFragment.java @@ -3,9 +3,7 @@ package com.nutomic.syncthingandroid.gui; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; import com.nutomic.syncthingandroid.R; diff --git a/src/main/java/com/nutomic/syncthingandroid/gui/WebGuiActivity.java b/src/main/java/com/nutomic/syncthingandroid/gui/WebGuiActivity.java index 31b93586..cbeeb872 100644 --- a/src/main/java/com/nutomic/syncthingandroid/gui/WebGuiActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/gui/WebGuiActivity.java @@ -12,7 +12,6 @@ import android.view.View; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; -import android.widget.TextView; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.syncthing.SyncthingService; diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java index 0e7e185b..3e85928e 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java @@ -37,6 +37,8 @@ public class GetTask extends AsyncTask { public static final String URI_MODEL = "/rest/model"; + public static final String URI_NODEID = "/rest/nodeid"; + /** * params[0] Syncthing hostname * params[1] URI to call diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index 3fde8929..b34173c3 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -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) { - 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; + public void editNode(final Node node, + final OnNodeIdNormalizedListener listener) { + mSyncthingService.getApi().normalizeNodeId(node.NodeID, + new RestApi.OnNodeIdNormalizedListener() { + @Override + public void onNodeIdNormalized(String normalizedId, String error) { + listener.onNodeIdNormalized(normalizedId, error); + if (normalizedId == null) + return; + + node.NodeID = normalizedId; + // If the node already exists, just update it. + boolean create = true; + 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; } + /** + * 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() { return mAvailableCount.get() == TOTAL_STARTUP_CALLS; } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index bf0a82e2..942ebe64 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -36,7 +36,6 @@ import java.io.InputStreamReader; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Iterator; -import java.util.concurrent.locks.ReentrantLock; /** * Holds the native syncthing instance and provides an API to access it.