diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 19861c3..7489cd6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,104 +1,131 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="ch.dissem.apps.abit">
-
-
-
-
-
+
+
+
+
+
+
+
+
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme">
+ android:name=".MessageListActivity"
+ android:label="@string/app_name">
-
+
-
+
+ android:name=".MessageDetailActivity"
+ android:label="@string/title_message_detail"
+ android:parentActivityName=".MessageListActivity"
+ tools:ignore="UnusedAttribute">
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".MessageListActivity" />
+ android:name=".SubscriptionDetailActivity"
+ android:label="@string/title_subscription_detail"
+ android:parentActivityName=".MessageListActivity"
+ tools:ignore="UnusedAttribute">
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".MessageListActivity" />
+ android:name=".ComposeMessageActivity"
+ android:label="Compose"
+ android:parentActivityName=".MessageListActivity">
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".MessageListActivity" />
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+ android:name=".SettingsActivity"
+ android:label="@string/settings"
+ android:parentActivityName=".MessageListActivity">
-
+
-
+
+ android:name=".OpenBitmessageLinkActivity"
+ android:label="@string/title_activity_open_bitmessage_link"
+ android:theme="@style/Theme.AppCompat.Light.Dialog">
-
+
-
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java b/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java
index 7ddfce9..3500931 100644
--- a/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java
+++ b/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java
@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ListView;
+
import ch.dissem.apps.abit.listeners.ListSelectionListener;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
@@ -54,6 +55,7 @@ public abstract class AbstractItemListFragment extends ListFragment {
* The current activated item position. Only used on tablets.
*/
private int activatedPosition = ListView.INVALID_POSITION;
+ private boolean activateOnItemClick;
abstract void updateList(Label label);
@@ -75,6 +77,17 @@ public abstract class AbstractItemListFragment extends ListFragment {
}
}
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // When setting CHOICE_MODE_SINGLE, ListView will automatically
+ // give items the 'activated' state when touched.
+ getListView().setChoiceMode(activateOnItemClick
+ ? ListView.CHOICE_MODE_SINGLE
+ : ListView.CHOICE_MODE_NONE);
+ }
+
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@@ -118,11 +131,15 @@ public abstract class AbstractItemListFragment extends ListFragment {
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
- // When setting CHOICE_MODE_SINGLE, ListView will automatically
- // give items the 'activated' state when touched.
- getListView().setChoiceMode(activateOnItemClick
- ? ListView.CHOICE_MODE_SINGLE
- : ListView.CHOICE_MODE_NONE);
+ this.activateOnItemClick = activateOnItemClick;
+
+ if (isVisible()) {
+ // When setting CHOICE_MODE_SINGLE, ListView will automatically
+ // give items the 'activated' state when touched.
+ getListView().setChoiceMode(activateOnItemClick
+ ? ListView.CHOICE_MODE_SINGLE
+ : ListView.CHOICE_MODE_NONE);
+ }
}
private void setActivatedPosition(int position) {
diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java
index 0ec6bfa..dbe16ea 100644
--- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java
+++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java
@@ -1,5 +1,8 @@
package ch.dissem.apps.abit;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -13,6 +16,8 @@ import android.widget.AdapterView;
import ch.dissem.apps.abit.listeners.ActionBarListener;
import ch.dissem.apps.abit.listeners.ListSelectionListener;
import ch.dissem.apps.abit.service.Singleton;
+import ch.dissem.apps.abit.synchronization.Authenticator;
+import ch.dissem.apps.abit.synchronization.SyncAdapter;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext;
@@ -66,6 +71,8 @@ public class MessageListActivity extends AppCompatActivity
private static final Logger LOG = LoggerFactory.getLogger(MessageListActivity.class);
private static final int ADD_IDENTITY = 1;
+ private Account account;
+
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
@@ -97,6 +104,10 @@ public class MessageListActivity extends AppCompatActivity
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
twoPane = true;
+
+ // In two-pane mode, list items should be given the
+ // 'activated' state when touched.
+ listFragment.setActivateOnItemClick(true);
}
createDrawer(toolbar);
@@ -107,17 +118,40 @@ public class MessageListActivity extends AppCompatActivity
if (getIntent().hasExtra(EXTRA_SHOW_MESSAGE)) {
onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE));
}
+
+ account = createSyncAccount(this);
+ getContentResolver().setSyncAutomatically(account, SyncAdapter.AUTHORITY, true);
+ }
+
+ private Account createSyncAccount(Context context) {
+ // Create the account type and default account
+ Account newAccount = new Account(Authenticator.ACCOUNT_NAME, Authenticator.ACCOUNT_TYPE);
+ // Get an instance of the Android account manager
+ AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
+ /*
+ * Add the account and account type, no password or user data
+ * If successful, return the Account object, otherwise report an error.
+ */
+ if (accountManager.addAccountExplicitly(newAccount, null, null)) {
+ /*
+ * If you don't set android:syncable="true" in
+ * in your element in the manifest,
+ * then call context.setIsSyncable(account, AUTHORITY, 1)
+ * here.
+ */
+ } else {
+ /*
+ * The account exists or some other error occurred. Log this, report it,
+ * or handle it internally.
+ */
+ LOG.error("Couldn't add account");
+ }
+ return newAccount;
}
@Override
protected void onResume() {
super.onResume();
- if (twoPane) {
- // In two-pane mode, list items should be given the
- // 'activated' state when touched.
- ((MessageListFragment) getSupportFragmentManager().findFragmentById(R.id.item_list))
- .setActivateOnItemClick(true);
- }
}
private void changeList(AbstractItemListFragment> listFragment) {
@@ -232,7 +266,7 @@ public class MessageListActivity extends AppCompatActivity
if (item.getTag() instanceof Label) {
selectedLabel = (Label) item.getTag();
if (!(getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof MessageListFragment)) {
- MessageListFragment listFragment = new MessageListFragment();
+ MessageListFragment listFragment = new MessageListFragment(getApplicationContext());
changeList(listFragment);
listFragment.updateList(selectedLabel);
} else {
diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
index cb02e54..a5515c5 100644
--- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
+++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
@@ -1,6 +1,7 @@
package ch.dissem.apps.abit;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
@@ -11,6 +12,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
import ch.dissem.apps.abit.listeners.ActionBarListener;
import ch.dissem.apps.abit.listeners.ListSelectionListener;
import ch.dissem.apps.abit.service.Singleton;
@@ -41,6 +43,10 @@ public class MessageListFragment extends AbstractItemListFragment {
public MessageListFragment() {
}
+ public MessageListFragment(Context ctx) {
+ bmc = Singleton.getBitmessageContext(ctx);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -58,6 +64,9 @@ public class MessageListFragment extends AbstractItemListFragment {
@Override
public void updateList(Label label) {
currentLabel = label;
+
+ if (!isVisible()) return;
+
setListAdapter(new ArrayAdapter(
getActivity(),
android.R.layout.simple_list_item_activated_1,
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java
new file mode 100644
index 0000000..5a6d3c3
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java
@@ -0,0 +1,82 @@
+package ch.dissem.apps.abit.synchronization;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.os.Bundle;
+
+/*
+ * Implement AbstractAccountAuthenticator and stub out all
+ * of its methods
+ */
+public class Authenticator extends AbstractAccountAuthenticator {
+ public static final String ACCOUNT_NAME = "Bitmessage";
+ public static final String ACCOUNT_TYPE = "bitmessage.dissem.ch";
+
+ // Simple constructor
+ public Authenticator(Context context) {
+ super(context);
+ }
+
+ // Editing properties is not supported
+ @Override
+ public Bundle editProperties(
+ AccountAuthenticatorResponse r, String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Don't add additional accounts
+ @Override
+ public Bundle addAccount(
+ AccountAuthenticatorResponse r,
+ String s,
+ String s2,
+ String[] strings,
+ Bundle bundle) throws NetworkErrorException {
+ return null;
+ }
+
+ // Ignore attempts to confirm credentials
+ @Override
+ public Bundle confirmCredentials(
+ AccountAuthenticatorResponse r,
+ Account account,
+ Bundle bundle) throws NetworkErrorException {
+ return null;
+ }
+
+ // Getting an authentication token is not supported
+ @Override
+ public Bundle getAuthToken(
+ AccountAuthenticatorResponse r,
+ Account account,
+ String s,
+ Bundle bundle) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ // Getting a label for the auth token is not supported
+ @Override
+ public String getAuthTokenLabel(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Updating user credentials is not supported
+ @Override
+ public Bundle updateCredentials(
+ AccountAuthenticatorResponse r,
+ Account account,
+ String s, Bundle bundle) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ // Checking features for the account is not supported
+ @Override
+ public Bundle hasFeatures(
+ AccountAuthenticatorResponse r,
+ Account account, String[] strings) throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/AuthenticatorService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/AuthenticatorService.java
new file mode 100644
index 0000000..8fe717d
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/AuthenticatorService.java
@@ -0,0 +1,31 @@
+package ch.dissem.apps.abit.synchronization;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * A bound Service that instantiates the authenticator
+ * when started.
+ */
+public class AuthenticatorService extends Service {
+ /**
+ * Instance field that stores the authenticator object
+ */
+ private Authenticator authenticator;
+
+ @Override
+ public void onCreate() {
+ // Create a new authenticator object
+ authenticator = new Authenticator(this);
+ }
+
+ /*
+ * When the system binds to this Service to make the RPC call
+ * return the authenticator's IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return authenticator.getIBinder();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java
new file mode 100644
index 0000000..c79f074
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java
@@ -0,0 +1,65 @@
+package ch.dissem.apps.abit.synchronization;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/*
+ * Define an implementation of ContentProvider that stubs out
+ * all methods
+ */
+public class StubProvider extends ContentProvider {
+ /*
+ * Always return true, indicating that the
+ * provider loaded correctly.
+ */
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ /*
+ * Return no type for MIME type
+ */
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+ /*
+ * query() always returns no results
+ *
+ */
+ @Override
+ public Cursor query(
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+ /*
+ * insert() always returns null (no URI)
+ */
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+ /*
+ * delete() always returns "no rows affected" (0)
+ */
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+ /*
+ * update() always returns "no rows affected" (0)
+ */
+ public int update(
+ Uri uri,
+ ContentValues values,
+ String selection,
+ String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java
new file mode 100644
index 0000000..890b097
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java
@@ -0,0 +1,74 @@
+package ch.dissem.apps.abit.synchronization;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SyncResult;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import ch.dissem.apps.abit.service.Singleton;
+import ch.dissem.bitmessage.BitmessageContext;
+
+/**
+ * Sync Adapter to synchronize with the Bitmessage network - fetches
+ * new objects and then disconnects.
+ */
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+ private final static Logger LOG = LoggerFactory.getLogger(SyncAdapter.class);
+
+ public static final String AUTHORITY = "ch.dissem.bitmessage.provider";
+
+ private final BitmessageContext bmc;
+
+ public SyncAdapter(Context context) {
+ super(context, true, false);
+ bmc = Singleton.getBitmessageContext(context);
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
+ // If the Bitmessage context acts as a full node, synchronization isn't necessary
+ if (bmc.isRunning()) return;
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+
+ String trustedNode = preferences.getString("trusted_node", null);
+ if (trustedNode == null) return;
+ trustedNode = trustedNode.trim();
+ if (trustedNode.isEmpty()) return;
+
+ int port;
+ if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$")) {
+ int index = trustedNode.lastIndexOf(':');
+ String portString = trustedNode.substring(index + 1);
+ trustedNode = trustedNode.substring(0, index);
+ try {
+ port = Integer.parseInt(portString);// FIXME
+ } catch (NumberFormatException e) {
+ LOG.error("Invalid port " + portString);
+ // TODO: show error as notification
+ return;
+ }
+ } else {
+ port = 8444;
+ }
+ long timeoutInSeconds = preferences.getInt("sync_timeout", 120);
+ try {
+ LOG.info("Synchronization started");
+ bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
+ LOG.info("Synchronization finished");
+ } catch (UnknownHostException e) {
+ LOG.error("Couldn't synchronize", e);
+ // TODO: show error as notification
+ }
+ }
+}
diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java
new file mode 100644
index 0000000..fb6fd9f
--- /dev/null
+++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java
@@ -0,0 +1,49 @@
+package ch.dissem.apps.abit.synchronization;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Define a Service that returns an IBinder for the
+ * sync adapter class, allowing the sync adapter framework to call
+ * onPerformSync().
+ */
+public class SyncService extends Service {
+ // Storage for an instance of the sync adapter
+ private static SyncAdapter syncAdapter = null;
+ // Object to use as a thread-safe lock
+ private static final Object syncAdapterLock = new Object();
+
+ /*
+ * Instantiate the sync adapter object.
+ */
+ @Override
+ public void onCreate() {
+ /*
+ * Create the sync adapter as a singleton.
+ * Set the sync adapter as syncable
+ * Disallow parallel syncs
+ */
+ synchronized (syncAdapterLock) {
+ if (syncAdapter == null) {
+ syncAdapter = new SyncAdapter(getApplicationContext());
+ }
+ }
+ }
+
+ /**
+ * Return an object that allows the system to invoke
+ * the sync adapter.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ /*
+ * Get the object that allows external processes
+ * to call onPerformSync(). The object is created
+ * in the base class code when the SyncAdapter
+ * constructors call super()
+ */
+ return syncAdapter.getSyncAdapterBinder();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp/activity_message_list.xml b/app/src/main/res/layout-w720dp/activity_message_list.xml
similarity index 100%
rename from app/src/main/res/layout-sw600dp/activity_message_list.xml
rename to app/src/main/res/layout-w720dp/activity_message_list.xml
diff --git a/app/src/main/res/layout/activity_open_bitmessage_link.xml b/app/src/main/res/layout/activity_open_bitmessage_link.xml
index 95f792d..fedbd9d 100644
--- a/app/src/main/res/layout/activity_open_bitmessage_link.xml
+++ b/app/src/main/res/layout/activity_open_bitmessage_link.xml
@@ -1,83 +1,81 @@
-
+
+ android:id="@+id/address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="BM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:id="@+id/label_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/address">
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/label"
+ android:inputType="textPersonName" />
+ android:id="@+id/import_contact"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label_wrapper"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="8dp"
+ android:text="@string/import_contact" />
+ android:id="@+id/subscribe"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/import_contact"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:text="@string/subscribe" />
+ android:id="@+id/do_import"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/subscribe"
+ android:layout_marginBottom="12dp"
+ android:layout_marginTop="12dp"
+ android:text="@string/do_import" />
+ android:id="@+id/compose_message"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/do_import"
+ android:layout_below="@+id/subscribe"
+ android:layout_toLeftOf="@+id/do_import"
+ android:layout_toStartOf="@+id/do_import"
+ android:text="@string/write_message" />
+ android:id="@+id/cancel"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/compose_message"
+ android:layout_toLeftOf="@+id/compose_message"
+ android:layout_toStartOf="@+id/compose_message"
+ android:text="@string/cancel" />
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 2222570..b86c0f8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -33,4 +33,8 @@
Stream #%d
Aktiv
Abonnement
+ Zeitbeschränkung der Synchronisierung
+ Timeout in Sekunden
+ Vertrauenswürdiger Knoten
+ Diese Adresse wird für die Synchronisation verwendet
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 43775b5..72fa388 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -33,4 +33,9 @@
Empty Trash
Stream #%d
Enabled
+ Trusted node
+ Use this node for synchronization
+ Synchronization Timeout
+ Timeout in seconds
+ Write message
diff --git a/app/src/main/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml
new file mode 100644
index 0000000..aa46bf6
--- /dev/null
+++ b/app/src/main/res/xml/authenticator.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e1f7327..d026e3f 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -9,8 +9,19 @@
android:entryValues="@array/connection_mode_values"/>
-->
+ android:defaultValue="true"
+ android:key="wifi_only"
+ android:summary="@string/wifi_only_summary"
+ android:title="@string/wifi_only" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/syncadapter.xml b/app/src/main/res/xml/syncadapter.xml
new file mode 100644
index 0000000..20cacd2
--- /dev/null
+++ b/app/src/main/res/xml/syncadapter.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file