Some sync adapter code and bugfixes around it - not yet functional
This commit is contained in:
parent
3c7fd02613
commit
348fa8daed
@ -3,9 +3,12 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="ch.dissem.apps.abit">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
|
||||
@ -99,6 +102,30 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name=".synchronization.StubProvider"
|
||||
android:authorities="ch.dissem.bitmessage.provider"
|
||||
android:exported="false"
|
||||
android:syncable="true" />
|
||||
<service android:name=".synchronization.AuthenticatorService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".synchronization.SyncService"
|
||||
android:exported="true"
|
||||
android:process=":sync">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/syncadapter" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -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<T> 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<T> 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,12 +131,16 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
|
||||
* given the 'activated' state when touched.
|
||||
*/
|
||||
public void setActivateOnItemClick(boolean activateOnItemClick) {
|
||||
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) {
|
||||
if (position == ListView.INVALID_POSITION) {
|
||||
|
@ -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 <provider> 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 {
|
||||
|
@ -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<Plaintext> {
|
||||
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<Plaintext> {
|
||||
@Override
|
||||
public void updateList(Label label) {
|
||||
currentLabel = label;
|
||||
|
||||
if (!isVisible()) return;
|
||||
|
||||
setListAdapter(new ArrayAdapter<Plaintext>(
|
||||
getActivity(),
|
||||
android.R.layout.simple_list_item_activated_1,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
@ -22,8 +21,8 @@
|
||||
android:id="@+id/label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPersonName"
|
||||
android:hint="@string/label"/>
|
||||
android:hint="@string/label"
|
||||
android:inputType="textPersonName" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
@ -31,53 +30,52 @@
|
||||
android:id="@+id/import_contact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/import_contact"
|
||||
android:layout_below="@+id/label_wrapper"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="8dp"/>
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/import_contact" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/subscribe"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/subscribe"
|
||||
android:layout_below="@+id/import_contact"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/compose_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Message"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_below="@+id/subscribe"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
android:text="@string/subscribe" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/do_import"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/do_import"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_below="@+id/subscribe"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_below="@+id/subscribe"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
android:text="@string/do_import" />
|
||||
|
||||
<Button
|
||||
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" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/cancel"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:layout_alignTop="@+id/do_import"
|
||||
android:layout_toLeftOf="@+id/do_import"
|
||||
android:layout_toStartOf="@+id/do_import"/>
|
||||
android:layout_alignTop="@+id/compose_message"
|
||||
android:layout_toLeftOf="@+id/compose_message"
|
||||
android:layout_toStartOf="@+id/compose_message"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -33,4 +33,8 @@
|
||||
<string name="stream_number">Stream #%d</string>
|
||||
<string name="enabled">Aktiv</string>
|
||||
<string name="title_subscription_detail">Abonnement</string>
|
||||
<string name="sync_timeout">Zeitbeschränkung der Synchronisierung</string>
|
||||
<string name="sync_timeout_summary">Timeout in Sekunden</string>
|
||||
<string name="trusted_node">Vertrauenswürdiger Knoten</string>
|
||||
<string name="trusted_node_summary">Diese Adresse wird für die Synchronisation verwendet</string>
|
||||
</resources>
|
@ -33,4 +33,9 @@
|
||||
<string name="empty_trash">Empty Trash</string>
|
||||
<string name="stream_number">Stream #%d</string>
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="trusted_node">Trusted node</string>
|
||||
<string name="trusted_node_summary">Use this node for synchronization</string>
|
||||
<string name="sync_timeout">Synchronization Timeout</string>
|
||||
<string name="sync_timeout_summary">Timeout in seconds</string>
|
||||
<string name="write_message">Write message</string>
|
||||
</resources>
|
||||
|
6
app/src/main/res/xml/authenticator.xml
Normal file
6
app/src/main/res/xml/authenticator.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="bitmessage.dissem.ch"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:smallIcon="@mipmap/ic_launcher" />
|
@ -9,8 +9,19 @@
|
||||
android:entryValues="@array/connection_mode_values"/>
|
||||
-->
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="wifi_only"
|
||||
android:title="@string/wifi_only"
|
||||
android:summary="@string/wifi_only_summary"
|
||||
android:defaultValue="true"/>
|
||||
android:title="@string/wifi_only" />
|
||||
<EditTextPreference
|
||||
android:inputType="textUri"
|
||||
android:key="trusted_node"
|
||||
android:summary="@string/trusted_node_summary"
|
||||
android:title="@string/trusted_node" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="120"
|
||||
android:inputType="number"
|
||||
android:key="sync_timeout"
|
||||
android:summary="@string/sync_timeout_summary"
|
||||
android:title="@string/sync_timeout" />
|
||||
</PreferenceScreen>
|
9
app/src/main/res/xml/syncadapter.xml
Normal file
9
app/src/main/res/xml/syncadapter.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<sync-adapter
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="ch.dissem.bitmessage.provider"
|
||||
android:accountType="ch.dissem.bitmessage"
|
||||
android:userVisible="true"
|
||||
android:supportsUploading="true"
|
||||
android:allowParallelSyncs="false"
|
||||
android:isAlwaysSyncable="true"/>
|
Loading…
Reference in New Issue
Block a user