Added node and repo settings activities.

This commit is contained in:
Felix Ableitner 2014-06-14 02:55:55 +02:00
parent 2ceca738ea
commit 3933740213
20 changed files with 893 additions and 13 deletions

View File

@ -45,5 +45,21 @@
<service android:name=".syncthing.SyncthingService" />
<activity
android:name=".gui.RepoSettingsActivity"
android:theme="@style/DialogWhenLargeCompat" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity
android:name=".gui.NodeSettingsActivity"
android:theme="@style/DialogWhenLargeCompat" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,22 @@
package com.nutomic.syncthingandroid;
import android.content.Context;
import android.preference.CheckBoxPreference;
/**
* Saves an extra object on construction, which can be retrieved later.
*/
public class ExtendedCheckBoxPreference extends CheckBoxPreference {
private final Object mObject;
public ExtendedCheckBoxPreference(Context context, Object object) {
super(context);
mObject = object;
}
public Object getObject() {
return mObject;
}
}

View File

@ -22,6 +22,7 @@ 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.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
@ -33,7 +34,7 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
/**
* {@link android.support.v4.app.ListFragment} that shows a configurable loading text.
*/
public abstract class LoadingListFragment extends Fragment implements RestApi.OnApiAvailableListener {
public abstract class LoadingListFragment extends Fragment implements RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
private boolean mInitialized = false;
@ -111,6 +112,7 @@ public abstract class LoadingListFragment extends Fragment implements RestApi.On
if (!mInitialized && getActivity() != null &&
activity.getApi() != null && mListFragment != null) {
onInitAdapter(activity);
getListView().setOnItemClickListener(this);
mInitialized = true;
}
}

View File

@ -32,7 +32,7 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
* {@link LocalNodeInfoFragment} in the navigation drawer.
*/
public class MainActivity extends ActionBarActivity
implements SyncthingService.OnWebGuiAvailableListener{
implements SyncthingService.OnWebGuiAvailableListener {
private SyncthingService mSyncthingService;
@ -197,7 +197,7 @@ public class MainActivity extends ActionBarActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@ -220,6 +220,16 @@ public class MainActivity extends ActionBarActivity
}
switch (item.getItemId()) {
case R.id.add_node:
Intent intent = new Intent(this, NodeSettingsActivity.class);
intent.setAction(NodeSettingsActivity.ACTION_CREATE);
startActivity(intent);
return true;
case R.id.add_repository:
intent = new Intent(this, RepoSettingsActivity.class);
intent.setAction(RepoSettingsActivity.ACTION_CREATE);
startActivity(intent);
return true;
case R.id.web_gui:
startActivity(new Intent(this, WebGuiActivity.class));
return true;

View File

@ -0,0 +1,228 @@
package com.nutomic.syncthingandroid.gui;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Shows node details and allows changing them.
*/
public class NodeSettingsActivity extends PreferenceActivity implements
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
RestApi.OnReceiveConnectionsListener, RestApi.OnApiAvailableListener {
public static final String ACTION_CREATE = "create";
public static final String ACTION_EDIT = "edit";
public static final String KEY_NODE_ID = "node_id";
private SyncthingService mSyncthingService;
private final ServiceConnection mSyncthingServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
mSyncthingService = binder.getService();
mSyncthingService.getApi()
.registerOnApiAvailableListener(NodeSettingsActivity.this);
}
public void onServiceDisconnected(ComponentName className) {
mSyncthingService = null;
}
};
private RestApi.Node mNode;
private EditTextPreference mNodeId;
private EditTextPreference mName;
private EditTextPreference mAddresses;
private Preference mVersion;
private Preference mCurrentAddress;
private Preference mDelete;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.node_settings);
mNodeId = (EditTextPreference) findPreference("node_id");
mNodeId.setOnPreferenceChangeListener(this);
mName = (EditTextPreference) findPreference("name");
mName.setOnPreferenceChangeListener(this);
mAddresses = (EditTextPreference) findPreference("addresses");
mAddresses.setOnPreferenceChangeListener(this);
mVersion = findPreference("version");
mVersion.setSummary("?");
mCurrentAddress = findPreference("current_address");
mCurrentAddress.setSummary("?");
mDelete = findPreference("delete");
mDelete.setOnPreferenceClickListener(this);
bindService(new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onApiAvailable() {
if (getIntent().getAction().equals(ACTION_CREATE)) {
setTitle(R.string.create_node);
mNode = new RestApi.Node();
mNode.Name = "";
mNode.NodeID = "";
mNode.Addresses = "dynamic";
getPreferenceScreen().removePreference(mDelete);
}
else if (getIntent().getAction().equals(ACTION_EDIT)) {
setTitle(R.string.edit_node);
mNodeId.setEnabled(false);
List<RestApi.Node> nodes = mSyncthingService.getApi().getNodes();
for (int i = 0; i < nodes.size(); i++) {
if (nodes.get(i).NodeID.equals(getIntent().getStringExtra(KEY_NODE_ID))) {
mNode = nodes.get(i);
break;
}
}
}
mSyncthingService.getApi().getConnections(NodeSettingsActivity.this);
mNodeId.setText(mNode.NodeID);
mNodeId.setSummary(mNode.NodeID);
mName.setText((mNode.Name));
mName.setSummary(mNode.Name);
mAddresses.setText(mNode.Addresses);
mAddresses.setSummary(mNode.Addresses);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.node_settings_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(getIntent().getAction().equals(ACTION_CREATE));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mNode.NodeID.equals("")) {
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();
return true;
}
mSyncthingService.getApi().editNode(mNode, true);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onDestroy() {
super.onDestroy();
unbindService(mSyncthingServiceConnection);
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
if (preference.equals(mNodeId)) {
mNode.NodeID = (String) o;
nodeUpdated();
return true;
}
else if (preference.equals(mName)) {
mNode.Name = (String) o;
nodeUpdated();
return true;
}
else if (preference.equals(mAddresses)) {
mNode.Addresses = (String) o;
nodeUpdated();
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDelete)) {
new AlertDialog.Builder(this)
.setMessage(R.string.delete_node_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteNode(mNode);
finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
}
return false;
}
/**
* Sets version and current address of the node.
*
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
if (connections.containsKey(mNode.NodeID)) {
mVersion.setSummary(connections.get(mNode.NodeID).ClientVersion);
mCurrentAddress.setSummary(connections.get(mNode.NodeID).Address);
}
}
/**
* Sends the updated node info if in edit mode.
*/
private void nodeUpdated() {
if (getIntent().getAction().equals(ACTION_EDIT)) {
mSyncthingService.getApi().editNode(mNode, false);
}
}
}

View File

@ -1,5 +1,11 @@
package com.nutomic.syncthingandroid.gui;
import android.content.Intent;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import com.nutomic.syncthingandroid.NodeAdapter;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi;
@ -12,7 +18,7 @@ import java.util.TimerTask;
* Displays a list of all existing nodes.
*/
public class NodesFragment extends LoadingListFragment implements
RestApi.OnApiAvailableListener {
RestApi.OnApiAvailableListener, ListView.OnItemClickListener {
private NodeAdapter mAdapter;
@ -58,4 +64,12 @@ public class NodesFragment extends LoadingListFragment implements
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), NodeSettingsActivity.class);
intent.setAction(NodeSettingsActivity.ACTION_EDIT);
intent.putExtra(NodeSettingsActivity.KEY_NODE_ID, mAdapter.getItem(i).NodeID);
startActivity(intent);
}
}

View File

@ -0,0 +1,280 @@
package com.nutomic.syncthingandroid.gui;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.nutomic.syncthingandroid.ExtendedCheckBoxPreference;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
import java.util.ArrayList;
import java.util.List;
/**
* Shows repo details and allows changing them.
*/
public class RepoSettingsActivity extends PreferenceActivity
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
RestApi.OnApiAvailableListener {
public static final String ACTION_CREATE = "create";
public static final String ACTION_EDIT = "edit";
public static final String KEY_REPOSITORY_ID = "repository_id";
private static final String KEY_NODE_SHARED = "node_shared";
private SyncthingService mSyncthingService;
private final ServiceConnection mSyncthingServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
mSyncthingService = binder.getService();
mSyncthingService.getApi()
.registerOnApiAvailableListener(RepoSettingsActivity.this);
}
public void onServiceDisconnected(ComponentName className) {
mSyncthingService = null;
}
};
private RestApi.Repository mRepository;
private EditTextPreference mRepositoryId;
private EditTextPreference mDirectory;
private CheckBoxPreference mRepositoryMaster;
private PreferenceScreen mNodes;
private CheckBoxPreference mVersioning;
private EditTextPreference mVersioningKeep;
private Preference mDelete;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.repo_settings);
mRepositoryId = (EditTextPreference) findPreference("repository_id");
mRepositoryId.setOnPreferenceChangeListener(this);
mDirectory = (EditTextPreference) findPreference("directory");
mDirectory.setOnPreferenceChangeListener(this);
mRepositoryMaster = (CheckBoxPreference) findPreference("repository_master");
mRepositoryMaster.setOnPreferenceChangeListener(this);
mNodes = (PreferenceScreen) findPreference("nodes");
mVersioning = (CheckBoxPreference) findPreference("versioning");
mVersioning.setOnPreferenceChangeListener(this);
mVersioningKeep = (EditTextPreference) findPreference("versioning_keep");
mVersioningKeep.setOnPreferenceChangeListener(this);
mDelete = findPreference("delete");
mDelete.setOnPreferenceClickListener(this);
bindService(new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onApiAvailable() {
if (getIntent().getAction().equals(ACTION_CREATE)) {
setTitle(R.string.create_repo);
mRepository = new RestApi.Repository();
mRepository.ID = "";
mRepository.Directory = "";
mRepository.Nodes = new ArrayList<RestApi.Node>();
mRepository.Versioning = new RestApi.Versioning();
getPreferenceScreen().removePreference(mDelete);
}
else if (getIntent().getAction().equals(ACTION_EDIT)) {
setTitle(R.string.edit_repo);
mRepositoryId.setEnabled(false);
List<RestApi.Repository> repos = mSyncthingService.getApi().getRepositories();
for (int i = 0; i < repos.size(); i++) {
if (repos.get(i).ID.equals(getIntent().getStringExtra(KEY_REPOSITORY_ID))) {
mRepository = repos.get(i);
break;
}
}
}
mRepositoryId.setText(mRepository.ID);
mRepositoryId.setSummary(mRepository.ID);
mDirectory.setText(mRepository.Directory);
mDirectory.setSummary(mRepository.Directory);
mRepositoryMaster.setChecked(mRepository.ReadOnly);
List<RestApi.Node> nodesList = mSyncthingService.getApi().getNodes();
for (RestApi.Node n : nodesList) {
ExtendedCheckBoxPreference cbp =
new ExtendedCheckBoxPreference(RepoSettingsActivity.this, n);
cbp.setTitle(n.Name);
cbp.setKey(KEY_NODE_SHARED);
cbp.setOnPreferenceChangeListener(RepoSettingsActivity.this);
cbp.setChecked(false);
for (RestApi.Node n2 : mRepository.Nodes) {
if (n2.NodeID.equals(n.NodeID)) {
cbp.setChecked(true);
}
}
mNodes.addPreference(cbp);
}
mVersioning.setChecked(mRepository.Versioning instanceof RestApi.SimpleVersioning);
if (mVersioning.isChecked()) {
mVersioningKeep.setText(mRepository.Versioning.getParams().get("keep"));
mVersioningKeep.setSummary(mRepository.Versioning.getParams().get("keep"));
mVersioningKeep.setEnabled(true);
}
else {
mVersioningKeep.setEnabled(false);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.repo_settings_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(getIntent().getAction().equals(ACTION_CREATE));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mRepository.ID.equals("")) {
Toast.makeText(this, R.string.repo_id_required, Toast.LENGTH_LONG).show();
return true;
}
if (mRepository.Directory.equals("")) {
Toast.makeText(this, R.string.repo_path_required, Toast.LENGTH_LONG).show();
return true;
}
mSyncthingService.getApi().editRepository(mRepository, true);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onDestroy() {
super.onDestroy();
unbindService(mSyncthingServiceConnection);
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
if (preference.equals(mRepositoryId)) {
mRepository.ID = (String) o;
repositoryUpdated();
return true;
}
else if (preference.equals(mDirectory)) {
mRepository.Directory = (String) o;
repositoryUpdated();
return true;
}
else if (preference.equals(mRepositoryMaster)) {
mRepository.ReadOnly = (Boolean) o;
repositoryUpdated();
return true;
}
else if (preference.getKey().equals(KEY_NODE_SHARED)) {
ExtendedCheckBoxPreference pref = (ExtendedCheckBoxPreference) preference;
RestApi.Node node = (RestApi.Node) pref.getObject();
if ((Boolean) o) {
mRepository.Nodes.add(node);
}
else {
for (RestApi.Node n : mRepository.Nodes) {
if (n.NodeID.equals(node.NodeID)) {
mRepository.Nodes.remove(n);
}
}
}
repositoryUpdated();
return true;
}
else if (preference.equals(mVersioning)) {
mVersioningKeep.setEnabled((Boolean) o);
if ((Boolean) o) {
RestApi.SimpleVersioning v = new RestApi.SimpleVersioning();
mRepository.Versioning = v;
v.setParams(5);
mVersioningKeep.setText("5");
mVersioningKeep.setSummary("5");
}
else {
mRepository.Versioning = new RestApi.Versioning();
}
repositoryUpdated();
return true;
}
else if (preference.equals(mVersioningKeep)) {
((RestApi.SimpleVersioning) mRepository.Versioning)
.setParams(Integer.parseInt((String) o));
repositoryUpdated();
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDelete)) {
new AlertDialog.Builder(this)
.setMessage(R.string.delete_repo_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteRepository(mRepository);
finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
}
return false;
}
private void repositoryUpdated() {
if (getIntent().getAction().equals(ACTION_EDIT)) {
mSyncthingService.getApi().editRepository(mRepository, false);
}
}
}

View File

@ -1,5 +1,9 @@
package com.nutomic.syncthingandroid.gui;
import android.content.Intent;
import android.view.View;
import android.widget.AdapterView;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.RepositoryAdapter;
import com.nutomic.syncthingandroid.syncthing.RestApi;
@ -12,7 +16,7 @@ import java.util.TimerTask;
* Displays a list of all existing repositories.
*/
public class RepositoriesFragment extends LoadingListFragment implements
RestApi.OnApiAvailableListener {
RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
private RepositoryAdapter mAdapter;
@ -58,4 +62,11 @@ public class RepositoriesFragment extends LoadingListFragment implements
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), RepoSettingsActivity.class);
intent.setAction(RepoSettingsActivity.ACTION_EDIT);
intent.putExtra(RepoSettingsActivity.KEY_REPOSITORY_ID, mAdapter.getItem(i).ID);
startActivity(intent);
}
}

View File

@ -1,5 +1,6 @@
package com.nutomic.syncthingandroid.gui;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
@ -93,6 +94,7 @@ public class SettingsActivity extends PreferenceActivity
* Manual target API as we manually check if ActionBar is available (for ActionBar back button).
*/
@Override
@SuppressLint("AppCompatMethod")
@TargetApi(11)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -106,7 +108,7 @@ public class SettingsActivity extends PreferenceActivity
bindService(new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
addPreferencesFromResource(R.xml.settings);
addPreferencesFromResource(R.xml.app_settings);
PreferenceScreen screen = getPreferenceScreen();
mVersion = screen.findPreference(SYNCTHING_VERSION_KEY);
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY);

View File

@ -38,7 +38,7 @@ public class PostTask extends AsyncTask<String, Void, Void> {
post.addHeader(new BasicHeader("X-API-Key", params[2]));
try {
if (params.length > 2) {
if (params.length > 3) {
post.setEntity(new StringEntity(params[3]));
}
httpclient.execute(post);

View File

@ -5,9 +5,12 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.nutomic.syncthingandroid.BuildConfig;
import com.nutomic.syncthingandroid.R;
import org.json.JSONArray;
@ -63,7 +66,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
public static class Repository {
public String Directory;
public String ID;
public final boolean IgnorePerms = true;
public String Invalid;
public List<Node> Nodes;
public boolean ReadOnly;
@ -236,14 +238,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
*/
public void shutdown() {
mNotificationManager.cancel(NOTIFICATION_RESTART);
new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey, "");
new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey);
}
/**
* Restarts the syncthing binary.
*/
public void restart() {
new PostTask().execute(mUrl, PostTask.URI_RESTART);
new PostTask().execute(mUrl, PostTask.URI_RESTART, mApiKey);
}
/**
@ -307,7 +309,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
* Sends the updated mConfig via Rest API to syncthing and displays a "restart" notification.
*/
private void configUpdated() {
new PostTask().execute(mUrl, PostTask.URI_CONFIG, mConfig.toString());
new PostTask().execute(mUrl, PostTask.URI_CONFIG, mApiKey, mConfig.toString());
Intent i = new Intent(mContext, SyncthingService.class)
.setAction(SyncthingService.ACTION_RESTART);
@ -352,6 +354,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
new GetTask() {
@Override
protected void onPostExecute(String s) {
if (s == null)
return;
try {
JSONObject system = new JSONObject(s);
SystemInfo si = new SystemInfo();
@ -395,6 +400,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
* Returns a list of all existing repositores.
*/
public List<Repository> getRepositories() {
if (mConfig == null)
return new ArrayList<Repository>();
List<Repository> ret = null;
try {
JSONArray repos = mConfig.getJSONArray("Repositories");
@ -404,16 +412,16 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
Repository r = new Repository();
r.Directory = json.getString("Directory");
r.ID = json.getString("ID");
// Hardcoded to true because missing permissions support.
// r.IgnorePerms = json.getBoolean("IgnorePerms");
r.Invalid = json.getString("Invalid");
r.Nodes = getNodes(json.getJSONArray("Nodes"));
r.ReadOnly = json.getBoolean("ReadOnly");
JSONObject versioning = json.getJSONObject("Versioning");
if (versioning.getString("Type").equals("simple")) {
Log.d(TAG, mConfig.toString());
SimpleVersioning sv = new SimpleVersioning();
JSONObject params = versioning.getJSONObject("Params");
Log.d(TAG, params.toString());
sv.setParams(params.getInt("keep"));
r.Versioning = sv;
}
@ -486,6 +494,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
new GetTask() {
@Override
protected void onPostExecute(String s) {
if (s == null)
return;
Long now = System.currentTimeMillis();
Long difference = (now - mPreviousConnectionTime) / 1000;
if (difference < 1) {
@ -547,6 +558,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
new GetTask() {
@Override
protected void onPostExecute(String s) {
if (s == null)
return;
try {
JSONObject json = new JSONObject(s);
Model m = new Model();
@ -571,4 +585,128 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
}.execute(mUrl, GetTask.URI_MODEL, mApiKey, "repo", repoId);
}
/**
* Updates or creates the given node.
*/
public void editNode(Node node, boolean create) {
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(" ")));
Log.d(TAG, n.toString());
Log.d(TAG, nodes.toString());
configUpdated();
}
catch (JSONException e) {
Log.w(TAG, "Failed to read nodes", e);
}
}
public void deleteNode(Node node) {
try {
JSONArray nodes = mConfig.getJSONArray("Nodes");
for (int i = 0; i < nodes.length(); i++) {
JSONObject json = nodes.getJSONObject(i);
if (node.NodeID.equals(json.getString("NodeID"))) {
mConfig.remove("Nodes");
mConfig.put("Nodes", delete(nodes, nodes.getJSONObject(i)));
break;
}
}
configUpdated();
}
catch (JSONException e) {
Log.w(TAG, "Failed to edit repo", e);
}
}
public void editRepository(Repository repository, boolean create) {
try {
JSONArray repos = mConfig.getJSONArray("Repositories");
JSONObject r = null;
if (create) {
r = new JSONObject();
repos.put(r);
}
else {
for (int i = 0; i < repos.length(); i++) {
JSONObject json = repos.getJSONObject(i);
if (repository.ID.equals(json.getString("ID"))) {
r = repos.getJSONObject(i);
break;
}
}
}
r.put("Directory", repository.Directory);
r.put("ID", repository.ID);
r.put("ReadOnly", repository.ReadOnly);
JSONArray nodes = new JSONArray();
for (Node n : repository.Nodes) {
JSONObject element = new JSONObject();
element.put("Addresses", n.Addresses);
element.put("Name", n.Name);
element.put("NodeID", n.NodeID);
nodes.put(element);
}
r.put("Nodes", nodes);
JSONObject versioning = new JSONObject();
versioning.put("Type", repository.Versioning.getType());
JSONObject params = new JSONObject();
versioning.put("Params", params);
for (String key : repository.Versioning.getParams().keySet()) {
params.put(key, repository.Versioning.getParams().get(key));
}
r.put("Versioning", versioning);
configUpdated();
}
catch (JSONException e) {
Log.w(TAG, "Failed to edit repo " + repository.ID + " at " + repository.Directory, e);
}
}
public void deleteRepository(Repository repository) {
try {
JSONArray repos = mConfig.getJSONArray("Repositories");
for (int i = 0; i < repos.length(); i++) {
JSONObject json = repos.getJSONObject(i);
if (repository.ID.equals(json.getString("ID"))) {
mConfig.remove("Repositories");
mConfig.put("Repositories", delete(repos, repos.getJSONObject(i)));
break;
}
}
configUpdated();
}
catch (JSONException e) {
Log.w(TAG, "Failed to edit repo", e);
}
}
private JSONArray delete(JSONArray array, JSONObject delete) throws JSONException {
JSONArray newArray = new JSONArray();
for (int i = 0; i < array.length(); i++) {
if (!array.getJSONObject(i).equals(delete)) {
newArray.put(array.get(i));
}
}
return newArray;
}
}

View File

@ -8,6 +8,18 @@
android:icon="@android:drawable/ic_menu_share"
android:title="@string/share_node_id"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_repository"
android:title="@string/add_repo"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_node"
android:title="@string/add_node"
app:showAsAction="ifRoom" />
<item
android:id="@+id/web_gui"
android:title="@string/web_gui_title" />

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/create"
android:title="@string/create" />
</menu>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/create"
android:title="@string/create" />
</menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DialogWhenLargeCompat" parent="android:Theme.Holo.DialogWhenLarge" />
</resources>

View File

@ -12,6 +12,14 @@
<!-- ActionBar title shown when the drawer is open -->
<string name="system_info">System Info</string>
<!-- MainActivity -->
<!-- Title of the "add repo" menu action -->
<string name="add_repo">Add Repository</string>
<!-- Title of the "add node" menu action -->
<string name="add_node">Add Node</string>
<!-- Title of the "share node id" menu action -->
<string name="share_node_id">Share Node ID</string>
@ -70,6 +78,43 @@
<!-- Title for announce server status -->
<string name="announce_server">Announce Server</string>
<!-- RepoSettingsActivity -->
<!-- Title for RepoSettingsActivity in create mode -->
<string name="create_repo">Create Repository</string>
<!-- Title for RepoSettingsActivity in edit mode -->
<string name="edit_repo">Edit Repository</string>
<!-- Used to confirm repo/node creation -->
<string name="create">Create</string>
<!-- Dialog shown when attempting to delete a repository -->
<string name="delete_repo_confirm">Do you really want to delete this repository?</string>
<!-- Toast shown when trying to create a repository with an empty ID -->
<string name="repo_id_required">The repository ID must not be empty</string>
<!-- Toast shown when trying to create a repository with an empty path -->
<string name="repo_path_required">The repository path must not be empty</string>
<!-- NodeSettingsActivity -->
<!-- Title for NodeSettingsActivity in create mode -->
<string name="create_node">Create Node</string>
<!-- Title for NodeSettingsActivity in edit mode -->
<string name="edit_node">Edit Node</string>
<!-- Dialog shown when attempting to delete a node -->
<string name="delete_node_confirm">Do you really want to delete this node?</string>
<!-- Toast shown when trying to create a node with an empty ID -->
<string name="node_id_required">The node ID must not be empty</string>
<!-- Toast shown when trying to create a node with an empty name -->
<string name="node_name_required">The node name must not be empty</string>
<!-- WebGuiActivity -->
<!-- Title of the web gui activity -->

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAppCompatDialogTheme" parent="DialogWhenLargeCompat">
<item name="android:windowIsFloating">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
<!--<item name="android:windowActionModeOverlay">true</item>-->
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
<EditTextPreference
android:key="node_id"
android:title="Node ID"
android:text="7TGPF3-AYRIE6-YVAHJN-TYWH5D-KFDF6B-7JUHMI-LNIC2S-YE5QIV-FH2Q" />
<EditTextPreference
android:key="name"
android:title="Name"
android:text="laptop" />
<EditTextPreference
android:key="addresses"
android:title="Addresses"
android:text="dynamic" />
<Preference
android:key="version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
<Preference
android:key="current_address"
android:title="Current Address"
style="?android:preferenceInformationStyle" />
<Preference
android:key="delete"
android:title="Delete Node" />
</PreferenceScreen>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
<EditTextPreference
android:key="repository_id"
android:title="Repository ID"
android:text="default" />
<EditTextPreference
android:key="directory"
android:title="Directory"
android:text="/sdcard/syncthing/" />
<CheckBoxPreference
android:key="repository_master"
android:title="Repository Master" />
<PreferenceScreen
android:key="nodes"
android:title="Nodes" />
<CheckBoxPreference
android:key="versioning"
android:title="File Versioning" />
<EditTextPreference
android:key="versioning_keep"
android:title="Keep Versions"
android:inputType="numberDecimal" />
<Preference
android:key="delete"
android:title="Delete Repository" />
</PreferenceScreen>