From 348fa8daed6659c0d751595bf499c5dcbe9501b0 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Thu, 8 Oct 2015 14:11:45 +0200 Subject: [PATCH 01/12] Some sync adapter code and bugfixes around it - not yet functional --- app/src/main/AndroidManifest.xml | 141 +++++++++++------- .../apps/abit/AbstractItemListFragment.java | 27 +++- .../dissem/apps/abit/MessageListActivity.java | 48 +++++- .../dissem/apps/abit/MessageListFragment.java | 9 ++ .../abit/synchronization/Authenticator.java | 82 ++++++++++ .../synchronization/AuthenticatorService.java | 31 ++++ .../abit/synchronization/StubProvider.java | 65 ++++++++ .../abit/synchronization/SyncAdapter.java | 74 +++++++++ .../abit/synchronization/SyncService.java | 49 ++++++ .../activity_message_list.xml | 0 .../layout/activity_open_bitmessage_link.xml | 122 ++++++++------- app/src/main/res/values-de/strings.xml | 4 + app/src/main/res/values/strings.xml | 5 + app/src/main/res/xml/authenticator.xml | 6 + app/src/main/res/xml/preferences.xml | 19 ++- app/src/main/res/xml/syncadapter.xml | 9 ++ 16 files changed, 556 insertions(+), 135 deletions(-) create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/AuthenticatorService.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java rename app/src/main/res/{layout-sw600dp => layout-w720dp}/activity_message_list.xml (100%) create mode 100644 app/src/main/res/xml/authenticator.xml create mode 100644 app/src/main/res/xml/syncadapter.xml 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<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, 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 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="16dp"> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="16dp"> <TextView - 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/address" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="BM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + android:textAppearance="?android:attr/textAppearanceSmall" /> <android.support.design.widget.TextInputLayout - android:id="@+id/label_wrapper" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/address"> + android:id="@+id/label_wrapper" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/address"> <EditText - android:id="@+id/label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textPersonName" - android:hint="@string/label"/> + android:id="@+id/label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/label" + android:inputType="textPersonName" /> </android.support.design.widget.TextInputLayout> <Switch - 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: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" /> <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_marginTop="8dp" - android:layout_marginBottom="8dp"/> + 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" /> <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: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" /> <Button - android:id="@+id/do_import" - 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_marginTop="12dp" - android:layout_marginBottom="12dp"/> + 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" - 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: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" /> </RelativeLayout> 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 @@ <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> \ 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 @@ <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> 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 @@ +<?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" /> \ 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"/> --> <SwitchPreference - android:key="wifi_only" - android:title="@string/wifi_only" - android:summary="@string/wifi_only_summary" - android:defaultValue="true"/> + android:defaultValue="true" + android:key="wifi_only" + android:summary="@string/wifi_only_summary" + 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> \ 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 @@ +<?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"/> \ No newline at end of file From 67c06b9884f2a9750bad6ff51a414960da400217 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 12 Oct 2015 14:44:01 +0200 Subject: [PATCH 02/12] Some sync adapter fixes and changes - still doesn't work --- app/src/main/AndroidManifest.xml | 3 +- .../dissem/apps/abit/MessageListActivity.java | 127 +++++++----------- .../dissem/apps/abit/service/Singleton.java | 1 - .../abit/synchronization/StubProvider.java | 7 + .../abit/synchronization/SyncAdapter.java | 25 +++- .../abit/synchronization/SyncService.java | 2 +- app/src/main/res/menu/compose.xml | 2 +- app/src/main/res/menu/main.xml | 10 -- app/src/main/res/values-de/strings.xml | 5 +- app/src/main/res/values/strings.xml | 4 +- 10 files changed, 82 insertions(+), 104 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7489cd6..f43e1e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -103,9 +103,10 @@ </intent-filter> </activity> + <!-- Synchronization --> <provider android:name=".synchronization.StubProvider" - android:authorities="ch.dissem.bitmessage.provider" + android:authorities="ch.dissem.apps.abit.provider" android:exported="false" android:syncable="true" /> <service android:name=".synchronization.AuthenticatorService"> 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 dbe16ea..bc40eec 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -2,27 +2,15 @@ package ch.dissem.apps.abit; import android.accounts.Account; import android.accounts.AccountManager; -import android.content.Context; +import android.content.ContentResolver; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; 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; -import ch.dissem.bitmessage.entity.Streamable; -import ch.dissem.bitmessage.entity.valueobject.Label; +import android.widget.CompoundButton; import com.mikepenz.community_material_typeface_library.CommunityMaterial; import com.mikepenz.google_material_typeface_library.GoogleMaterial; @@ -34,10 +22,11 @@ import com.mikepenz.materialdrawer.accountswitcher.AccountHeaderBuilder; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem; -import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; +import com.mikepenz.materialdrawer.model.SwitchDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IProfile; import com.mikepenz.materialdrawer.model.interfaces.Nameable; +import com.mikepenz.materialdrawer.model.interfaces.OnCheckedChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +34,17 @@ import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.ArrayList; +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.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.valueobject.Label; + +import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY; + /** * An activity representing a list of Messages. This activity @@ -69,10 +69,9 @@ public class MessageListActivity extends AppCompatActivity public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"; private static final Logger LOG = LoggerFactory.getLogger(MessageListActivity.class); + private static final long SYNC_FREQUENCY = 15 * 60; // seconds 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. @@ -82,7 +81,6 @@ public class MessageListActivity extends AppCompatActivity private AccountHeader accountHeader; private BitmessageContext bmc; private Label selectedLabel; - private Menu menu; @Override protected void onCreate(Bundle savedInstanceState) { @@ -119,39 +117,22 @@ public class MessageListActivity extends AppCompatActivity onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE)); } - account = createSyncAccount(this); - getContentResolver().setSyncAutomatically(account, SyncAdapter.AUTHORITY, true); + createSyncAccount(); } - 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"); + private void createSyncAccount() { + // Create account, if it's missing. (Either first run, or user has deleted account.) + Account account = new Account(Authenticator.ACCOUNT_NAME, Authenticator.ACCOUNT_TYPE); + + if (AccountManager.get(this).addAccountExplicitly(account, null, null)) { + // Inform the system that this account supports sync + ContentResolver.setIsSyncable(account, AUTHORITY, 1); + // Inform the system that this account is eligible for auto sync when the network is up + ContentResolver.setSyncAutomatically(account, AUTHORITY, true); + // Recommend a schedule for automatic synchronization. The system may modify this based + // on other scheduled syncs and network utilization. + ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), SYNC_FREQUENCY); } - return newAccount; - } - - @Override - protected void onResume() { - super.onResume(); } private void changeList(AbstractItemListFragment<?> listFragment) { @@ -253,12 +234,24 @@ public class MessageListActivity extends AppCompatActivity .withAccountHeader(accountHeader) .withDrawerItems(drawerItems) .addStickyDrawerItems( - new SecondaryDrawerItem() + new PrimaryDrawerItem() .withName(R.string.subscriptions) .withIcon(CommunityMaterial.Icon.cmd_rss_box), - new SecondaryDrawerItem() + new PrimaryDrawerItem() .withName(R.string.settings) - .withIcon(GoogleMaterial.Icon.gmd_settings) + .withIcon(GoogleMaterial.Icon.gmd_settings), + new SwitchDrawerItem() + .withName(R.string.full_node) + .withIcon(CommunityMaterial.Icon.cmd_cloud_outline) + .withOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { + // TODO: warn user, option to restrict to WiFi + if (isChecked && !bmc.isRunning()) bmc.startup(); + else if (bmc.isRunning()) bmc.shutdown(); + } + }) + .withChecked(bmc.isRunning()) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override @@ -288,6 +281,8 @@ public class MessageListActivity extends AppCompatActivity case R.string.settings: startActivity(new Intent(MessageListActivity.this, SettingsActivity.class)); break; + case R.string.full_node: + return true; } } return false; @@ -297,36 +292,6 @@ public class MessageListActivity extends AppCompatActivity .build(); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - this.menu = menu; - updateMenu(); - return true; - } - - private void updateMenu() { - boolean running = bmc.isRunning(); - menu.findItem(R.id.sync_enabled).setVisible(running); - menu.findItem(R.id.sync_disabled).setVisible(!running); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.sync_disabled: - bmc.startup(); - updateMenu(); - return true; - case R.id.sync_enabled: - bmc.shutdown(); - updateMenu(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - /** * Callback method from {@link ListSelectionListener} * indicating that the item with the given ID was selected. diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index 76c3301..e16b235 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -1,6 +1,5 @@ package ch.dissem.apps.abit.service; -import android.app.NotificationManager; import android.content.Context; import ch.dissem.apps.abit.listeners.MessageListener; 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 index c79f074..f5b3d2d 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/StubProvider.java @@ -10,6 +10,8 @@ import android.net.Uri; * all methods */ public class StubProvider extends ContentProvider { + public static final String AUTHORITY = "ch.dissem.apps.abit.provider"; + /* * Always return true, indicating that the * provider loaded correctly. @@ -18,6 +20,7 @@ public class StubProvider extends ContentProvider { public boolean onCreate() { return true; } + /* * Return no type for MIME type */ @@ -25,6 +28,7 @@ public class StubProvider extends ContentProvider { public String getType(Uri uri) { return null; } + /* * query() always returns no results * @@ -38,6 +42,7 @@ public class StubProvider extends ContentProvider { String sortOrder) { return null; } + /* * insert() always returns null (no URI) */ @@ -45,6 +50,7 @@ public class StubProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { return null; } + /* * delete() always returns "no rows affected" (0) */ @@ -52,6 +58,7 @@ public class StubProvider extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } + /* * update() always returns "no rows affected" (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 index 890b097..c12d30c 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -25,17 +25,32 @@ import ch.dissem.bitmessage.BitmessageContext; 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); + /** + * Set up the sync adapter + */ + public SyncAdapter(Context context, boolean autoInitialize) { + super(context, autoInitialize); + bmc = Singleton.getBitmessageContext(context); + } + + /** + * Set up the sync adapter. This form of the + * constructor maintains compatibility with Android 3.0 + * and later platform versions + */ + public SyncAdapter( + Context context, + boolean autoInitialize, + boolean allowParallelSyncs) { + super(context, autoInitialize, allowParallelSyncs); bmc = Singleton.getBitmessageContext(context); } @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { + LOG.info("Synchronizing Bitmessage"); // If the Bitmessage context acts as a full node, synchronization isn't necessary if (bmc.isRunning()) return; @@ -52,7 +67,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { String portString = trustedNode.substring(index + 1); trustedNode = trustedNode.substring(0, index); try { - port = Integer.parseInt(portString);// FIXME + port = Integer.parseInt(portString); } catch (NumberFormatException e) { LOG.error("Invalid port " + portString); // 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 index fb6fd9f..4beaa04 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -27,7 +27,7 @@ public class SyncService extends Service { */ synchronized (syncAdapterLock) { if (syncAdapter == null) { - syncAdapter = new SyncAdapter(getApplicationContext()); + syncAdapter = new SyncAdapter(getApplicationContext(), true); } } } diff --git a/app/src/main/res/menu/compose.xml b/app/src/main/res/menu/compose.xml index ee25c89..dae8007 100644 --- a/app/src/main/res/menu/compose.xml +++ b/app/src/main/res/menu/compose.xml @@ -5,5 +5,5 @@ android:id="@+id/send" app:showAsAction="always" android:icon="@drawable/ic_action_send" - android:title="@string/disable_sync"/>` + android:title="@string/send"/>` </menu> \ No newline at end of file diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index 9f35ce1..9eb7100 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -1,14 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> - <item - android:id="@+id/sync_enabled" - app:showAsAction="always" - android:icon="@drawable/ic_action_notification_sync" - android:title="@string/disable_sync"/> - <item - android:id="@+id/sync_disabled" - app:showAsAction="always" - android:icon="@drawable/ic_action_notification_sync_disabled" - android:title="@string/enable_sync"/> </menu> \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b86c0f8..43a43cc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -5,8 +5,6 @@ <string name="wifi_only">Nur WLAN</string> <string name="wifi_only_summary">Nicht mit Mobilfunknetz verbinden</string> <string name="bitmessage_active">Bitmessage ist aktiv</string> - <string name="disable_sync">Synchronisieren ausschalten</string> - <string name="enable_sync">Synchronisieren einschalten</string> <string name="subject">Betreff</string> <string name="to">An</string> <string name="title_message_detail">Nachricht</string> @@ -37,4 +35,7 @@ <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> + <string name="full_node">Aktiver Knoten</string> + <string name="send">Senden</string> + <string name="write_message">Schreiben</string> </resources> \ 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 72fa388..adf6794 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,8 +2,6 @@ <string name="app_name">Abit</string> <string name="title_message_detail">Message Detail</string> <string name="title_subscription_detail">Subscription Detail</string> - <string name="disable_sync">Disable Sync</string> - <string name="enable_sync">Enable Sync</string> <string name="bitmessage_active">Bitmessage active</string> <string name="wifi_mode">Wi-Fi Connection Mode</string> <string name="settings">Settings</string> @@ -38,4 +36,6 @@ <string name="sync_timeout">Synchronization Timeout</string> <string name="sync_timeout_summary">Timeout in seconds</string> <string name="write_message">Write message</string> + <string name="full_node">Full node</string> + <string name="send">Send</string> </resources> From ed5fb69eafef6fadac59b9bf431869c8d1d661f2 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 18 Oct 2015 21:47:07 +0200 Subject: [PATCH 03/12] Bugfixes --- .../dissem/apps/abit/MessageListActivity.java | 41 +++++++++---------- .../dissem/apps/abit/MessageListFragment.java | 24 ++++++----- .../abit/repository/AndroidInventory.java | 2 +- 3 files changed, 35 insertions(+), 32 deletions(-) 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 1da5bf0..70891ea 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -12,15 +12,6 @@ import android.view.View; import android.widget.AdapterView; import android.widget.CompoundButton; -import ch.dissem.apps.abit.listener.ActionBarListener; -import ch.dissem.apps.abit.listener.ListSelectionListener; -import ch.dissem.apps.abit.notification.NetworkNotification; -import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.BitmessageContext; -import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.entity.Plaintext; -import ch.dissem.bitmessage.entity.valueobject.Label; - import com.mikepenz.community_material_typeface_library.CommunityMaterial; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.iconics.IconicsDrawable; @@ -42,11 +33,10 @@ import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.ArrayList; -import java.util.Timer; -import java.util.TimerTask; -import ch.dissem.apps.abit.listeners.ActionBarListener; -import ch.dissem.apps.abit.listeners.ListSelectionListener; +import ch.dissem.apps.abit.listener.ActionBarListener; +import ch.dissem.apps.abit.listener.ListSelectionListener; +import ch.dissem.apps.abit.notification.NetworkNotification; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; import ch.dissem.bitmessage.BitmessageContext; @@ -277,14 +267,7 @@ public class MessageListActivity extends AppCompatActivity public boolean onItemClick(AdapterView<?> adapterView, View view, int i, long l, IDrawerItem item) { if (item.getTag() instanceof Label) { selectedLabel = (Label) item.getTag(); - if (!(getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof MessageListFragment)) { - MessageListFragment listFragment = new MessageListFragment(getApplicationContext()); - changeList(listFragment); - listFragment.updateList(selectedLabel); - } else { - ((MessageListFragment) getSupportFragmentManager() - .findFragmentById(R.id.item_list)).updateList(selectedLabel); - } + showSelectedLabel(); return false; } else if (item instanceof Nameable<?>) { Nameable<?> ni = (Nameable<?>) item; @@ -300,6 +283,10 @@ public class MessageListActivity extends AppCompatActivity case R.string.settings: startActivity(new Intent(MessageListActivity.this, SettingsActivity.class)); break; + case R.string.archive: + selectedLabel = null; + showSelectedLabel(); + break; case R.string.full_node: return true; } @@ -311,6 +298,17 @@ public class MessageListActivity extends AppCompatActivity .build(); } + private void showSelectedLabel() { + if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof MessageListFragment) { + ((MessageListFragment) getSupportFragmentManager() + .findFragmentById(R.id.item_list)).updateList(selectedLabel); + } else { + MessageListFragment listFragment = new MessageListFragment(getApplicationContext()); + changeList(listFragment); + listFragment.updateList(selectedLabel); + } + } + /** * Callback method from {@link ListSelectionListener} * indicating that the item with the given ID was selected. @@ -354,6 +352,7 @@ public class MessageListActivity extends AppCompatActivity @Override public void updateTitle(CharSequence title) { + //noinspection ConstantConditions getSupportActionBar().setTitle(title); } 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 c31b091..5de7c7e 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java @@ -1,25 +1,24 @@ package ch.dissem.apps.abit; -import android.app.Activity; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; -import android.support.v4.app.ListFragment; -import android.view.*; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; -import android.widget.ListView; import android.widget.TextView; + import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ListSelectionListener; - -import ch.dissem.apps.abit.listeners.ActionBarListener; -import ch.dissem.apps.abit.listeners.ListSelectionListener; import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.BitmessageContext; -import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.MessageRepository; @@ -45,6 +44,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { public MessageListFragment() { } + @SuppressLint("ValidFragment") public MessageListFragment(Context ctx) { bmc = Singleton.getBitmessageContext(ctx); } @@ -98,7 +98,11 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { } }); if (getActivity() instanceof ActionBarListener) { - ((ActionBarListener) getActivity()).updateTitle(label.toString()); + if (label != null) { + ((ActionBarListener) getActivity()).updateTitle(label.toString()); + } else { + ((ActionBarListener) getActivity()).updateTitle(getString(R.string.archive)); + } } if (emptyTrashMenuItem != null) { emptyTrashMenuItem.setVisible(label != null && label.getType() == Label.Type.TRASH); diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidInventory.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidInventory.java index 32b9ed8..5e5f1c5 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidInventory.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidInventory.java @@ -187,7 +187,7 @@ public class AndroidInventory implements Inventory { "hash = X'" + object.getInventoryVector() + "'", null, null, null, null ); - return c.getColumnCount() > 0; + return c.getCount() > 0; } @Override From e149efcfff135fb9ce5ce9087361f73b7ad89417 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 20 Oct 2015 21:17:52 +0200 Subject: [PATCH 04/12] Changed string resource --- .../ch/dissem/apps/abit/notification/NetworkNotification.java | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java index b51cc47..99f78d6 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java @@ -27,7 +27,7 @@ public class NetworkNotification extends AbstractNotification { bmc = Singleton.getBitmessageContext(ctx); builder = new NotificationCompat.Builder(ctx); builder.setSmallIcon(R.drawable.ic_notification_full_node) - .setContentTitle(ctx.getString(R.string.bitmessage_active)) + .setContentTitle(ctx.getString(R.string.bitmessage_full_node)) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 28b28b6..383adde 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -4,7 +4,7 @@ <string name="settings">Einstellungen</string> <string name="wifi_only">Nur WLAN</string> <string name="wifi_only_summary">Nicht mit Mobilfunknetz verbinden</string> - <string name="bitmessage_active">Bitmessage ist aktiv</string> + <string name="bitmessage_full_node">Bitmessage Netzknoten</string> <string name="subject">Betreff</string> <string name="to">An</string> <string name="title_message_detail">Nachricht</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 727b3eb..b493dc2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,7 @@ <string name="app_name">Abit</string> <string name="title_message_detail">Message Detail</string> <string name="title_subscription_detail">Subscription Detail</string> - <string name="bitmessage_active">Bitmessage active</string> + <string name="bitmessage_full_node">Bitmessage Node</string> <string name="wifi_mode">Wi-Fi Connection Mode</string> <string name="settings">Settings</string> <string name="wifi_only">Wi-Fi only</string> From f19996f79c06baafa6d9b3ed1a001f8aa3d4c122 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 21 Oct 2015 16:15:57 +0200 Subject: [PATCH 05/12] Removed unused icons --- .../ic_action_notification_sync.png | Bin 967 -> 0 bytes .../ic_action_notification_sync_disabled.png | Bin 1059 -> 0 bytes .../ic_action_notification_sync.png | Bin 580 -> 0 bytes .../ic_action_notification_sync_disabled.png | Bin 641 -> 0 bytes .../ic_action_notification_sync.png | Bin 1296 -> 0 bytes .../ic_action_notification_sync_disabled.png | Bin 1388 -> 0 bytes .../ic_action_notification_sync.png | Bin 1943 -> 0 bytes .../ic_action_notification_sync_disabled.png | Bin 2048 -> 0 bytes .../ic_action_notification_sync.png | Bin 2779 -> 0 bytes .../ic_action_notification_sync_disabled.png | Bin 2864 -> 0 bytes 10 files changed, 0 insertions(+), 0 deletions(-) delete mode 100755 app/src/main/res/drawable-hdpi/ic_action_notification_sync.png delete mode 100755 app/src/main/res/drawable-hdpi/ic_action_notification_sync_disabled.png delete mode 100755 app/src/main/res/drawable-mdpi/ic_action_notification_sync.png delete mode 100755 app/src/main/res/drawable-mdpi/ic_action_notification_sync_disabled.png delete mode 100755 app/src/main/res/drawable-xhdpi/ic_action_notification_sync.png delete mode 100755 app/src/main/res/drawable-xhdpi/ic_action_notification_sync_disabled.png delete mode 100755 app/src/main/res/drawable-xxhdpi/ic_action_notification_sync.png delete mode 100755 app/src/main/res/drawable-xxhdpi/ic_action_notification_sync_disabled.png delete mode 100755 app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync.png delete mode 100755 app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync_disabled.png diff --git a/app/src/main/res/drawable-hdpi/ic_action_notification_sync.png b/app/src/main/res/drawable-hdpi/ic_action_notification_sync.png deleted file mode 100755 index d938029163421b0b20417517e44df7ed0ef7cff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 967 zcmV;&133JNP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm000AyNkl<ZXhZFp zPw1s%6vsc`8h`RH$)FgKSSTs8m}X;?hFHwV!pKI#&O%7@FKRGM!(?GeWHHU`W^6E0 z*kFMu5?M?ke<q2cA$9bf&aJ20eSi0Tp5J?a)2;i|?RM|`JkL3w=X=h1&wa0$^PxHS zp9R3FR&b%fLV;zTz)V9ZBCeA3-7<Y#a{0^v>^$&{q`T)1Af4X<TrX+uTmg(u9=O_$ z&lNy<^2Mp&1{|04_bikcEC<F<zHs&@;1A#^@CERJr0<7<84|!Te`gQJeg)nI4oUj9 z$NZJ4LJ@H-up3y@)2U;?i;~{#Ie+4`!4&ZB5pj`ACvZbyZ5?niV8EO5b!J}w4>%}k zUn|6*010s=uzdtkR-uSk1-Pu<3mDA95Kl;Yy9J^NAR;cbVM)JFGFmDzsf<R%1;9>V zL#}r?J=BsDtpJY!mjgQ_{n-I=QVB^9c3d~a(}fZTB<-zLp&B3}E(Kl&9PfvLJ(B+E zQbN)%T~0-W!^rXPoN#q|pti)e0NevSnoc=x9lws_>XA9I3%E5GzaVLU%|<N%&jPok zk!_NW)EFF}8xb3U=kog_lD5_CR|OCeX9KT{`W#?$t>hB`F3V8#rEh@yYub)lfJ=Z^ zGyZLGy`+C@PHY0eX~0MMz1xoamdcy)^|b)LAsN8$9_w1;f8<C+oDO`LpMOpOzxNuo z0B2<gU6Z?SlJr9_OC6IUq6{JTMh>CB`tDf;;7udK3FpM~*edCrzQ!iaiwGA47Y2{n zCUF9kW%MoJ$<{E+GWt|e)mEg8T5{jWRs$!1M<jjRrGi7Gt5QY88CDV41UT+}Bm1#N z(drON6&?q+q#--MQ_^o8A@aK0Y)M~tTt7G4@S~ERuW{aUTLJug_a(qhoyYs`YbHTN z#3#U)*%wXxB^D9q1FIytsjC`I0I9<5j(;02lz0vJw9JVF@DxLIDpBQiF}WEaA>0$) zRX{i=-pkil8P{cgEU82b#IgVq!cQF@F2ZS=A5K=c;h#;L*ysAP0TRMFaR+d7R;OvU z-vD+=ifI$4T^|HsWF+sZHv-oIR{-Y$?w`uxyR8)%<eBN90Hv1^;kM%(pnU4^c|I>4 z#QEkjI!)Fw5SLMpK~H{UV`d5zVP$~9Pkto<4h$Ld<La-30yF)SW99%g`O-3X0nl=4 pL#|mUFyv{r?6pv!<<y2;^FQ(<H9&Q%-n;++002ovPDHLkV1hf%z1#o* diff --git a/app/src/main/res/drawable-hdpi/ic_action_notification_sync_disabled.png b/app/src/main/res/drawable-hdpi/ic_action_notification_sync_disabled.png deleted file mode 100755 index c8bcece5bc975b31264cb7f77d415c01cd302bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1059 zcmV+;1l;?HP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm000B(Nkl<ZXhZFo zNyr^l5QV>k8k}$;8V3|XP*Biq5JWKIfD2tHiXy={p%_qN9O6RMg#(yuL<9#E6$K%S zECh`hag_wYl?dX(8I229Do#*xLiMB5fA`;gU*|mu^o8*F)3<NcsZ(`sRg1%X=`i=7 z3Bdo&;Kabhz{m(pDKs*GqhFsG7#+fq2P}<&h_J0wfumOO`EOvqq!@~;2tY)f51a?= zl=N@YNJN|lTmXC`>96|ph&Ud25jeBMIq!cN*s%}*>HMR>*=gv_3PePl2y6r{1O5ay zNcyb>z#G6>EdVO#XJ;OA1|s5WU@LGeV1qA7dfkRHx{oD*Z|4B;Jn&W<t9?L9Q^*mv zqfQ5}nc;{y2DleE64)-u`8-<*vW<aHCEeZUXxH;n0N%eI*qjA<5cLsZ=jo^qgKh)x zkC~P9@jM`k08|Dd;s)U1{QE#U^`$_A0l)KO){~F<8Q3K0>rxQ&0@woFR5R9Fgf#$` zQmf5-z~jKWJm(!r&n+B4M4SP<0-Ow(eh0y+vnmq_!0)|Zu@w<^emih<vT(a3voUxT zGY}Cs0}rHyUBKsni?C8BM|XuBMueYV4D7DSuYo@aT$$%RE9u?A-e>em_FFQa2Cm4L zEUO;^*8%s{CAP07YV~wE@JtmDNj`J%Dq<iajsso=&PfZ`O8Oz&1Q+(EJ|>!;WlHYM z|Nj!WL((5Z5tIUO0lo&TO#>HIdY>RxFi~B0T@FQrd!xPi-1)yz(l3hwa7qI3uh+V! z=0`;5W!L6ZM63b!<g@(=z`ui5DFZG<r_i~1`ZbcQ=d+bebY6BnDfAU!CHj4k_lKZ} zACcrDUa=ZXbV_VQSP(1>Ub_}r#I_I-Hv$i(!4KRU)nw7>a24<o;6gMLzN}g$Hd~Ln zjBb<k_R#2iUr`DrfYURLT*z*nH%r==xqKIJ32<B0PDjM`T@srB?$2A@g=+;mC?)&A z6az>Q^;TkEUPe}+4cX(&=7Bzm%`WzdoW!>EIoy0cF8~iUFVy7pd%)9}Z`Kv_K$pbU zS_*fnYb5=;6adM>rCEH<8MkYoaj<U5%dYC3THB5i5OXn58Hfm9Y8N&Pt~QZ9m_>eI zZfH(+C5~5n=K@eGSkJ-D)dj#Q<`MLymUosjQfBH{D(}pKts+_XsSkj^X6y#O18!}b z>`Is@VjyMDdF+2}&fB&N`kt1r5+`Ik{aMxXw_J9<2X-tHz!4cS|8^}$_@^P?c4}>h zFFP~$l%y{f2B75NlD+xK=9^OIZKEiKN}O8O-U-06BHd+YVqjumWCW%Z8X3URuTKn& d4&lfH{sWr?uRudbvR?oI002ovPDHLkV1fpW-(~;+ diff --git a/app/src/main/res/drawable-mdpi/ic_action_notification_sync.png b/app/src/main/res/drawable-mdpi/ic_action_notification_sync.png deleted file mode 100755 index f051d5ec3d3de66def49e39d2461e33039baa43b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80006DNkl<ZSV!%a zJ!=+06osE7f=v`Z3L7C+RP3xoN#{RM15wDAg%&Dku+k<?>@<Z6`2bCbm_QQ$K}r>D ztV9zlAyptjqA4Qil^f=T@g;e8c2*Js?^Mg~?A~Y2oO^b}E)VT;eVPD!TLCk>Ch2t* zA7v}x#eqAL&hI#YnLPw9g~AtsHA#PRKuRlMW><mxQL$BE8TbUuNm?iak^|7|(r;7b zColtyOY-Z&Q5wL%XlB=eo4|g+J<tlY13kb&;IluMv|dDz1Yl+@@rulBAK*%Ok#n^o zW_A`B08RieB|WPQU}h%)H(kG^m8jSzgrpa7kpM9Sn<;yv$P<MMnArp1GB70RRndyV z7WDzQgDD?1dZ!^kka`k04RlKSnh4MaOb0J?H)_!$fWyEna0Iv@X(JKf0Pqg*7VVJq zeY^Kk0KBb^y4xlF+GzkcTFw@@ai@SUKv(t_1VhzK_*Bw(q5@sO-O45m0H=U4z_b3I zq>puNcMBMn^gC+NW55$&P|{*{7epWQ==I=P@}P+!%<Ka&u?56G;gw~=2%rTR3GMj9 zvmSU0d;{JCzWNDC&r`N2o>Le^br2pfR{#gvYK%&9(BzQ}5P`TSJQ>db$3u(O9zQO7 zLMjbV_b)gE%!hk#Bn>7nJxt67h(O!}-lAD&!~X$L>0nwJpseKe^P2!o1^xooe8C~E S!y|M60000<MNUMnLSTY+>F)9X diff --git a/app/src/main/res/drawable-mdpi/ic_action_notification_sync_disabled.png b/app/src/main/res/drawable-mdpi/ic_action_notification_sync_disabled.png deleted file mode 100755 index d8e45269053600a30ca7004c3c5cb9aee6fffa9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 641 zcmV-{0)G98P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80006=Nkl<ZSV!%a z&5Mmu9LGOjk{L;e@seaiENm4kWn=5DB*HWc%8n9$fP^dtMN>m)C`5^cvSe>X>@_T; z$Xmuw-`ja~dS>o@ZqL1SOZU{R+kN?+@A<x+NA!59$NSR<&}ZQPVIU%gfZ3%~O-cIL zDrqelh=^^#3SdIg9}kL%b--5OhNNHVvjN~Xusqvr3b-a|q85ON@M#_-03I|R5%Yji zU>UGS(vJ+lJz!<F*_8yc0le9&Vqh(B7FYu`1_BEJGxJE2Pia=nKn`FM*e&UMRnb+N zFd_`Xr(SGIXF62|z&`j2ECj|RUF`+{Yd!|d2Yx4A$MeA%fQP^<;9~lIMAEz35+r(0 z1G|97z{XsT%$Rd6i=?ONKZk*1mPL{!tnGnWz%5`sus2Qdz*WuUNB}I`UEnovKvL8- zp|!jZECRMj`k6doZ7oS79p&K>VMbm8pHf;rcLKofwgES1xu2GfLXIpIFS-H165Iqf zSc2S8iHRBINM&^gc#|Y(+6C>z@RTzRT$Xe_op2}c1XvABlOrPb0mofrlFrmNVOG74 zq1Opu6R^E#!i<S>SzQ3mNqXB6fCS<wFamr5T*n_uRa|)DUpjyRN#8myq6A>j)e9_1 z1K*bb%mGdUJDQbMtrVoqeJLFQhO;4M07YC4#HV&8cjYp23J#^0-xtf5&Hn5*bSE4H zo|Ve#k|bZ#9W~)nAm7}>T0UqbW1a^L;CXETRR(JHy`LwF>$)cZ+67-(MC}f$`@24X bJ_CON+;#UMEIbiV00000NkvXXu0mjfMFkG{ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_notification_sync.png b/app/src/main/res/drawable-xhdpi/ic_action_notification_sync.png deleted file mode 100755 index 7e1ffedb3e0c682c98bd9b7ee51d8b07c5cf2d1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1296 zcmV+r1@HQaP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000EnNkl<ZcwX(A zc?f4y6vsbbdrZmN*o!P#iewsVBuT;_{4th>7(2~O#vhbjQll7TFk?-Yk{C&ZKWtgj z$d(zQEXfin$$oUkeRX@@@Auxkci;Q{`n})%_ub{3&pqefbIyID=S@A=pDBQ;1@xT2 z)CGEO0MkQo>H?#50W(`y(%++$VPqEI$^*wrTG2ZIX0{%1J}_6(0lfkccK!n3Bw&W5 z{d)r-Gkf3)U>#SyHvnR@&y?>CfV|n~O#TGmci?A9e{}5*t=|GgW}j2?3*cAa2Vgbu z9<Wl<FI~WB6@Z!f{C?n_5u1Gl#eM^x2cD4heue(EyR-_xr82WqfpdXPYDoDF@FMVl zq#tXHFCEeafDM2-!2D8a26uQ3SR(1u26Ge+Z5=?is+r9N<`r1NEMPNWd%%ODLx4RC zT;^+Fv7}d0Lg*5JTf!;8nK6WX9AIW!0keUVfn9ST9Kw7_pQnP*8Gvve{LdW*1wv-F zU2vNtbIQ5}T$}1lNCv>nb^|_>^jF#L#O^RG5;U_x=f^{mZZ2D=%J(Dy%xoXva^Q8~ zj<OUtD7MBOBpo(P(9rjK;E2c_{C2o3#TA(fz*WHUz(2rsz%vyg%#rk9*-iy_SPbkQ z^<5$9nzFHF-x~ukvl+m1z(!#)hj59cm&+Dko9||J9B^f11s*IN-85oM0pL*JmPnb8 zfJKtNT^j|<`kL9+z)iqjQQrkkuf-Gqys+W-d)DrjbXS=|b-tU~*}!?x*yWOz)|o3C z(+Gf>ZRo)XaBw8VJV`5?2sLP|ne7YQ6McUbxU@+Y*%W{WEpGmML_!@S>ARKyxEnkh z&HXXpZt#8G^_l|M3AhdLpe1XwCH+}fpkd=eQRU63kEhZXOZuwrdQAcB0Nftp_)Lab zWmjNbVa;rP;Eia!D}IrrFYC_P6hM&+_|@dajpQxd1Y_j_9;*5w=owtr&XV+Qa_#@C zE$jy@kLLC?*$+W4snu$YEWlSZvkOCpmkDsEq-C|FDH&vDURXIN>NeVIFaXF0LLY=c zsP@TR#KFvF25a+LLDt415Go3VJzae|r4^Xj;Q_#!_XJ=(Lg8QqR|6iaXKhvRO)p>! zZD!`BvSpIIGCZ&qRF|KW<cU|K*7ytv06ZPO5pXm0<_XbD{sL4MrJ0$h&<_LmR)pYH z=7WG6#wHyO5EcTb<+fu9@aQlIX7)~a7<o*R_b;;;e^}jFlD{D|Zb>X40N4WX{QSV2 zR4)LxN-A`R03aLUwSiK1XsmTKK{5aV!Y&~<*fv_j-Qg495s%{XaeEGc-{+OwAr*wA z00IOraV^T_2idvsA>hHw+kl6$KLKy$lyL}^+#v;o<NzW|SP%}Yx^i~n4y^zP5H<%+ z2fVDkO$`ce3HN#aFUj4gmDU=7OxjQ}J`p%7svA~O#+AMw@X)%oAE{ag5P|TB(UY0I zLjh$=U}Ip%0veU8%T-7Y>(d#4Scp*T-xTnY+EY0J{l9?6tGq8XRo=&s1V9dk2SQo% zo%h3UX_XhINdwTC*~j3<#R57t`*8!{K~bk>KW+g0O|kSXF@?Vu%`pxxFw!5UM-HH^ zovk|W6hKu{liF`;0h7A3RrQ)$Kvhzc+HYzBle)82_4*fLEvZn6@o_@{0000<MNUMn GLSTXw9$41^ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_notification_sync_disabled.png b/app/src/main/res/drawable-xhdpi/ic_action_notification_sync_disabled.png deleted file mode 100755 index 00e51afb9c718c973d5f96824a12309840087a71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1388 zcmV-y1(W)TP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000FuNkl<ZcwX(9 zS?H%z7{@<fj6KGMB}*d_289cakS*H<H!euF##l3r3$m4MvW2qm=E8+sP!l1M3tN<; zM3x~jqbwK7PCR;a-s$xI-{t(?bLv08e($;Y)$cv$InVPw&vw2?%=plZ+s_EV=m9g1 zz!(BE?f_#F97A9Xff+|&41pPUfUydUA+RBZfSGL!><Vm_sMl}6x03!%oIht;mIs*G zZopzmFZU^6W?KP^fG;Hd*)-qG76P{cd-ll--|q!pkn~UD{Bi)8**?H^z}~<;z|)fc zs_$TC`vIo{#{*vjmq_}l1AqsE%r2XL1s<35<WR$_b%Q*>%(id^00+T`E3^uDDh9#K zb_Z?*_7C$uEdW5{UPFNx3cx17?ZA<+zLLy26*F`B=KvQ2zNQ2Kt=={Sgh2rKMrP&) zxnXAm8%3MXQP9ke0&WG~_OOxUV9gmJ2XuwNvyyHZVrbi%!2kphJZYU3PPvlR77m93 zUrPF}O_>A$z5-TAT3ZT)EC3=;=naPyg%bd9F|PnOOZquQX5XA_03r~)d>vieaH4o^ z0KmQJec<q#+*Ohu=rb<mdno`806aE#u5CI|#;yTSt$ikxTL-twHS2(@C4H1)WY?Th z09+58(y~!<&8`8k-h0e!7vL^nuV|0el2*<;0A{vzh|5hQWxU+EvYpkTu6zLCPT=)= zlVfHl2T!PE{tg@`$$tlrY!5KABY?Xj4L<fH<;ta0hgkCf=E1-_^{&#)b^sh4?<}ja zT#}d8!J`BK-U}>?G<a0fBcV?yQXMMUW_CVsQM8x0!w(I%KI-BI$uKj&wcP<67717; z$usvv%~Fa2W_BR(L^Sse;0j59r>t+9ljQ+swtaB_>Q3N$oFwV{=+u&%r9LA<7`znC z`vka5(hq%W+rH-l;Md#x0MF9ZSW-WcQsEHfyc_N1w@c3hgFC}q0GtoFKad!eUWYW@ z&nLM%w4IijE${>xeRsnzmGsSgc|cPLoFM7PCWS(oOS(I>tltrMaYG6Lul=3`<5u-N zNgs5aV$xN*1%QJB4^e%7D<r{2hAG7>vO*wa`g$3BSkhx10QjEH{Uu$cV>xDaA#g$T z-~5)r0DzZ3=jN*6{rTl_B?$Aj3<S!Xev_Lt9L&t!-y5OD(OzBwyc7-|B|O0Cu~&i3 zgBH%v-X!Vy+UczT+=X4~|2e@)s)Lz1pWq#*hmemy0M9qS47NVE5vn#ev-)M|v)~2m z%*;>TJ2+>K=1^jjRENMUd3Wg`JSWNd#;lR;0pTQ_^z`d+)epEAycTG%Gl4S#*Wbtq zP7)4gb}n#?Ctpb)W?5g#0|E%k0aq>_?*YygR1Y1kq-bh51fq43B&Wx+M-dMQ0Bj3f z3mn^_RUa#yOtfZO9t0EvAb{Y{T3!*nMQ$3M00bHP0yorNhbCf{CIP1?0Kov~0!|zP zK)77F@k<)kCo@a)0zfU>x9QLq0(*pKwmP>M%bSR3?E|BT2L$7nM%lg;0Djr|9Po#g z&Uc4CL)*TW0>GP`(-NMwQ$1k2aOqhMcu!H3S&G5P0>I4tGIV-qa#9Xq4RBS*>+m)q zfv$<k&W>j3nv|#cF2!;I@UrEZKW2ox_VZiY+9B`x4b75!)0H_~fGe5>K+2JqoD(%m zI{_;tIVF}grV4;iX5H%+<h}<~s7)OJS;yV5cm$v{qSGdC^nhtQvC{gE9#9(5X_Gg4 uz_guMX?;fzD2?c}$s0Xj+D@#rzW)ILk~Ux0$Pj@50000<MNUMnLSTaM1bc=6 diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_notification_sync.png b/app/src/main/res/drawable-xxhdpi/ic_action_notification_sync.png deleted file mode 100755 index 4017dabe97591ca592e6766d3ba181cdee6726c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1943 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 b2kwH&+SJ@hcTJ-`-~tQ}ANN+*NdA8TlG;*a diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_notification_sync_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_action_notification_sync_disabled.png deleted file mode 100755 index 549cf6d8876b9c3e9df0717146cd3380f869ffa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 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 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync.png b/app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync.png deleted file mode 100755 index bea081263614e56ec9345eac00e3893eb99a09e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2779 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 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync_disabled.png b/app/src/main/res/drawable-xxxhdpi/ic_action_notification_sync_disabled.png deleted file mode 100755 index d71a99a1a5a5d1a496fe9e073aef42eb494008b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2864 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 From 9b1bf6bdb329a69448f11f2a459196d7a7ef08e3 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 21 Oct 2015 16:43:13 +0200 Subject: [PATCH 06/12] Moving Bitmessage context into a foreground service (work in progress) --- app/src/main/AndroidManifest.xml | 3 +- .../dissem/apps/abit/MessageListActivity.java | 16 ++--- .../notification/AbstractNotification.java | 2 +- .../notification/NetworkNotification.java | 10 +++- .../abit/synchronization/SyncAdapter.java | 19 +----- .../abit/synchronization/SyncService.java | 60 ++++++++++++++++++- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f43e1e6..f635f00 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,8 +119,7 @@ </service> <service android:name=".synchronization.SyncService" - android:exported="true" - android:process=":sync"> + android:exported="true"> <intent-filter> <action android:name="android.content.SyncAdapter"/> </intent-filter> 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 70891ea..f488cdf 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -36,9 +36,9 @@ import java.util.ArrayList; import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ListSelectionListener; -import ch.dissem.apps.abit.notification.NetworkNotification; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; +import ch.dissem.apps.abit.synchronization.SyncService; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; @@ -70,7 +70,7 @@ public class MessageListActivity extends AppCompatActivity public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"; private static final Logger LOG = LoggerFactory.getLogger(MessageListActivity.class); - private static final long SYNC_FREQUENCY = 15 * 60; // seconds + private static final long SYNC_FREQUENCY = 15;// FIXME * 60; // seconds private static final int ADD_IDENTITY = 1; /** @@ -253,14 +253,14 @@ public class MessageListActivity extends AppCompatActivity .withOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { - // TODO: warn user, option to restrict to WiFi - if (isChecked && !bmc.isRunning()) { - bmc.startup(); - new NetworkNotification(MessageListActivity.this).show(); - } else if (bmc.isRunning()) bmc.shutdown(); + if (isChecked) { + startService(new Intent(MessageListActivity.this, SyncService.class)); + } else { + stopService(new Intent(MessageListActivity.this, SyncService.class)); + } } }) - .withChecked(bmc.isRunning()) + .withChecked(SyncService.isRunning()) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java index 60bb86d..60eb282 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java @@ -10,7 +10,7 @@ import android.content.Context; public abstract class AbstractNotification { protected final Context ctx; protected final NotificationManager manager; - public Notification notification; + protected Notification notification; public AbstractNotification(Context ctx) { diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java index 99f78d6..a013602 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java @@ -1,6 +1,7 @@ package ch.dissem.apps.abit.notification; import android.annotation.SuppressLint; +import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -19,6 +20,8 @@ import ch.dissem.bitmessage.utils.Property; * Shows the network status (as long as the client is connected as a full node) */ public class NetworkNotification extends AbstractNotification { + public static final int ONGOING_NOTIFICATION_ID = 2; + private final BitmessageContext bmc; private NotificationCompat.Builder builder; @@ -31,6 +34,11 @@ public class NetworkNotification extends AbstractNotification { .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); } + public Notification getNotification() { + update(); + return notification; + } + @SuppressLint("StringFormatMatches") private boolean update() { boolean running = bmc.isRunning(); @@ -82,6 +90,6 @@ public class NetworkNotification extends AbstractNotification { @Override protected int getNotificationId() { - return 2; + return ONGOING_NOTIFICATION_ID; } } 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 index c12d30c..efc1333 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -30,22 +30,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Set up the sync adapter */ - public SyncAdapter(Context context, boolean autoInitialize) { - super(context, autoInitialize); - bmc = Singleton.getBitmessageContext(context); - } - - /** - * Set up the sync adapter. This form of the - * constructor maintains compatibility with Android 3.0 - * and later platform versions - */ - public SyncAdapter( - Context context, - boolean autoInitialize, - boolean allowParallelSyncs) { - super(context, autoInitialize, allowParallelSyncs); - bmc = Singleton.getBitmessageContext(context); + public SyncAdapter(Context context, BitmessageContext bitmessageContext) { + super(context, true); + bmc = bitmessageContext; } @Override 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 index 4beaa04..f744299 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -1,20 +1,44 @@ package ch.dissem.apps.abit.synchronization; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.os.IBinder; +import ch.dissem.apps.abit.MessageListActivity; +import ch.dissem.apps.abit.listener.MessageListener; +import ch.dissem.apps.abit.notification.NetworkNotification; +import ch.dissem.apps.abit.repository.AndroidAddressRepository; +import ch.dissem.apps.abit.repository.AndroidInventory; +import ch.dissem.apps.abit.repository.AndroidMessageRepository; +import ch.dissem.apps.abit.repository.SqlHelper; +import ch.dissem.apps.abit.service.Singleton; +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; +import ch.dissem.bitmessage.ports.MemoryNodeRegistry; +import ch.dissem.bitmessage.security.sc.SpongySecurity; + +import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; + /** * 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 { + private static MessageListener messageListener = null; + private static BitmessageContext bmc = null; // 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(); + private static volatile boolean running = false; + + public static boolean isRunning() { + return running; + } + /* * Instantiate the sync adapter object. */ @@ -26,12 +50,46 @@ public class SyncService extends Service { * Disallow parallel syncs */ synchronized (syncAdapterLock) { + final Context ctx = getApplicationContext(); + if (bmc == null) { +// messageListener = new MessageListener(ctx); +// SqlHelper sqlHelper = new SqlHelper(ctx); +// bmc = new BitmessageContext.Builder() +// .security(new SpongySecurity()) +// .nodeRegistry(new MemoryNodeRegistry()) +// .inventory(new AndroidInventory(sqlHelper)) +// .addressRepo(new AndroidAddressRepository(sqlHelper)) +// .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) +// .networkHandler(new DefaultNetworkHandler()) +// .listener(messageListener) +// .build(); + // FIXME: this needs to change once I figured out how to get rid of those singletons + messageListener = Singleton.getMessageListener(ctx); + bmc = Singleton.getBitmessageContext(ctx); + } if (syncAdapter == null) { - syncAdapter = new SyncAdapter(getApplicationContext(), true); + syncAdapter = new SyncAdapter(ctx, bmc); } } } + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // TODO: warn user, option to restrict to WiFi + running = true; + NetworkNotification networkNotification = new NetworkNotification(this); + startForeground(ONGOING_NOTIFICATION_ID, networkNotification.getNotification()); + bmc.startup(); + networkNotification.show(); + return Service.START_STICKY; + } + + @Override + public void onDestroy() { + bmc.shutdown(); + running = false; + } + /** * Return an object that allows the system to invoke * the sync adapter. From f5bf5c8bca6fdb665bd2644ef5bbd4a8848d3532 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Fri, 23 Oct 2015 22:40:09 +0200 Subject: [PATCH 07/12] Moving Bitmessage context into a foreground service (work in progress) --- app/src/main/AndroidManifest.xml | 1 + .../apps/abit/AbstractItemListFragment.java | 19 +- .../apps/abit/MessageDetailFragment.java | 19 +- .../dissem/apps/abit/MessageListActivity.java | 115 +++++++++-- .../dissem/apps/abit/MessageListFragment.java | 9 +- .../apps/abit/OpenBitmessageLinkActivity.java | 79 +++++++- .../apps/abit/SubscriptionDetailFragment.java | 3 +- .../apps/abit/SubscriptionListFragment.java | 6 +- .../notification/NetworkNotification.java | 6 +- .../notification/NewMessageNotification.java | 3 +- .../repository/AndroidMessageRepository.java | 21 +- .../dissem/apps/abit/service/Singleton.java | 67 +++++-- .../synchronization/BitmessageService.java | 186 ++++++++++++++++++ .../abit/synchronization/SyncService.java | 50 +---- 14 files changed, 441 insertions(+), 143 deletions(-) create mode 100644 app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f635f00..577db59 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -102,6 +102,7 @@ <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity> + <service android:name=".synchronization.BitmessageService" /> <!-- Synchronization --> <provider 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 6e25693..7b03a55 100644 --- a/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/AbstractItemListFragment.java @@ -17,14 +17,13 @@ package ch.dissem.apps.abit; import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.view.View; import android.widget.ListView; import ch.dissem.apps.abit.listener.ListSelectionListener; -import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.valueobject.Label; /** @@ -45,7 +44,6 @@ public abstract class AbstractItemListFragment<T> extends ListFragment { public void onItemSelected(Object plaintext) { } }; - protected BitmessageContext bmc; /** * The fragment's current callback object, which is notified of list item * clicks. @@ -59,13 +57,6 @@ public abstract class AbstractItemListFragment<T> extends ListFragment { abstract void updateList(Label label); - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - bmc = Singleton.getBitmessageContext(getActivity()); - } - @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -89,15 +80,15 @@ public abstract class AbstractItemListFragment<T> extends ListFragment { } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); + public void onAttach(Context context) { + super.onAttach(context); // Activities containing this fragment must implement its callbacks. - if (!(activity instanceof ListSelectionListener)) { + if (!(context instanceof ListSelectionListener)) { throw new IllegalStateException("Activity must implement fragment's callbacks."); } - callbacks = (ListSelectionListener) activity; + callbacks = (ListSelectionListener) context; } @Override diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java index 4652f6b..c43ae5f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java @@ -6,12 +6,15 @@ import android.support.v4.app.Fragment; import android.view.*; import android.widget.ImageView; import android.widget.TextView; + import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.util.Drawables; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.MessageRepository; + import com.mikepenz.google_material_typeface_library.GoogleMaterial; import java.util.Iterator; @@ -84,7 +87,7 @@ public class MessageDetailFragment extends Fragment { } } if (removed) { - Singleton.getBitmessageContext(inflater.getContext()).messages().save(item); + Singleton.getMessageRepository(inflater.getContext()).save(item); } return rootView; } @@ -103,7 +106,7 @@ public class MessageDetailFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem menuItem) { - BitmessageContext bmc = Singleton.getBitmessageContext(getActivity()); + MessageRepository messageRepo = Singleton.getMessageRepository(getContext()); switch (menuItem.getItemId()) { case R.id.reply: Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); @@ -113,21 +116,21 @@ public class MessageDetailFragment extends Fragment { return true; case R.id.delete: if (isInTrash(item)) { - bmc.messages().remove(item); + messageRepo.remove(item); } else { item.getLabels().clear(); - item.addLabels(bmc.messages().getLabels(Label.Type.TRASH)); - bmc.messages().save(item); + item.addLabels(messageRepo.getLabels(Label.Type.TRASH)); + messageRepo.save(item); } getActivity().onBackPressed(); return true; case R.id.mark_unread: - item.addLabels(bmc.messages().getLabels(Label.Type.UNREAD)); - bmc.messages().save(item); + item.addLabels(messageRepo.getLabels(Label.Type.UNREAD)); + messageRepo.save(item); return true; case R.id.archive: item.getLabels().clear(); - bmc.messages().save(item); + messageRepo.save(item); return true; default: return false; 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 f488cdf..3899a60 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -2,9 +2,17 @@ package ch.dissem.apps.abit; import android.accounts.Account; import android.accounts.AccountManager; +import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -38,12 +46,17 @@ import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ListSelectionListener; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; +import ch.dissem.apps.abit.synchronization.BitmessageService; import ch.dissem.apps.abit.synchronization.SyncService; -import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.AddressRepository; +import ch.dissem.bitmessage.ports.MessageRepository; +import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_START_NODE; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_STOP_NODE; import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY; @@ -79,15 +92,36 @@ public class MessageListActivity extends AppCompatActivity */ private boolean twoPane; + private Messenger messenger = new Messenger(new IncomingHandler()); + private Messenger service; + private boolean bound; + private ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + MessageListActivity.this.service = new Messenger(service); + MessageListActivity.this.bound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + service = null; + bound = false; + } + }; + private AccountHeader accountHeader; - private BitmessageContext bmc; private Label selectedLabel; + private MessageRepository messageRepo; + private AddressRepository addressRepo; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - bmc = Singleton.getBitmessageContext(this); - selectedLabel = bmc.messages().getLabels().get(0); + messageRepo = Singleton.getMessageRepository(this); + addressRepo = Singleton.getAddressRepository(this); + + selectedLabel = messageRepo.getLabels().get(0); setContentView(R.layout.activity_message_list); @@ -152,7 +186,7 @@ public class MessageListActivity extends AppCompatActivity private void createDrawer(Toolbar toolbar) { final ArrayList<IProfile> profiles = new ArrayList<>(); - for (BitmessageAddress identity : bmc.addresses().getIdentities()) { + for (BitmessageAddress identity : addressRepo.getIdentities()) { LOG.info("Adding identity " + identity.getAddress()); profiles.add(new ProfileDrawerItem() .withIcon(new Identicon(identity)) @@ -183,16 +217,12 @@ public class MessageListActivity extends AppCompatActivity @Override public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) { if (profile.getIdentifier() == ADD_IDENTITY) { - BitmessageAddress identity = bmc.createIdentity(false); - IProfile newProfile = new ProfileDrawerItem() - .withName(identity.toString()) - .withEmail(identity.getAddress()) - .withTag(identity); - if (accountHeader.getProfiles() != null) { - //we know that there are 2 setting elements. set the new profile above them ;) - accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); - } else { - accountHeader.addProfiles(newProfile); + try { + Message message = Message.obtain(null, BitmessageService.MSG_CREATE_IDENTITY); + message.replyTo = messenger; + service.send(message); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); } } // false if it should close the drawer @@ -202,7 +232,7 @@ public class MessageListActivity extends AppCompatActivity .build(); ArrayList<IDrawerItem> drawerItems = new ArrayList<>(); - for (Label label : bmc.messages().getLabels()) { + for (Label label : messageRepo.getLabels()) { PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag(label); switch (label.getType()) { case INBOX: @@ -254,13 +284,21 @@ public class MessageListActivity extends AppCompatActivity @Override public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { if (isChecked) { - startService(new Intent(MessageListActivity.this, SyncService.class)); + try { + service.send(Message.obtain(null, MSG_START_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } else { - stopService(new Intent(MessageListActivity.this, SyncService.class)); + try { + service.send(Message.obtain(null, MSG_STOP_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } } }) - .withChecked(SyncService.isRunning()) + .withChecked(BitmessageService.isRunning()) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override @@ -303,7 +341,7 @@ public class MessageListActivity extends AppCompatActivity ((MessageListFragment) getSupportFragmentManager() .findFragmentById(R.id.item_list)).updateList(selectedLabel); } else { - MessageListFragment listFragment = new MessageListFragment(getApplicationContext()); + MessageListFragment listFragment = new MessageListFragment(); changeList(listFragment); listFragment.updateList(selectedLabel); } @@ -360,4 +398,41 @@ public class MessageListActivity extends AppCompatActivity return selectedLabel; } + @Override + protected void onStart() { + super.onStart(); + bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + if (bound) { + unbindService(connection); + bound = false; + } + super.onStop(); + } + + private class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case BitmessageService.MSG_CREATE_IDENTITY: + BitmessageAddress identity = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); + IProfile newProfile = new ProfileDrawerItem() + .withName(identity.toString()) + .withEmail(identity.getAddress()) + .withTag(identity); + if (accountHeader.getProfiles() != null) { + //we know that there are 2 setting elements. set the new profile above them ;) + accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); + } else { + accountHeader.addProfiles(newProfile); + } + break; + default: + super.handleMessage(msg); + } + } + } } 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 5de7c7e..802b901 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java @@ -44,11 +44,6 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { public MessageListFragment() { } - @SuppressLint("ValidFragment") - public MessageListFragment(Context ctx) { - bmc = Singleton.getBitmessageContext(ctx); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,7 +68,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, - bmc.messages().findMessages(label)) { + Singleton.getMessageRepository(getContext()).findMessages(label)) { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { @@ -138,7 +133,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> { case R.id.empty_trash: if (currentLabel.getType() != Label.Type.TRASH) return true; - MessageRepository repo = bmc.messages(); + MessageRepository repo = Singleton.getMessageRepository(getContext()); for (Plaintext message : repo.findMessages(currentLabel)) { repo.remove(message); } diff --git a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java index cff4772..8f87f8a 100644 --- a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java @@ -17,19 +17,52 @@ package ch.dissem.apps.abit; import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.net.Uri; import android.os.Bundle; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Switch; import android.widget.TextView; -import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.BitmessageContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.dissem.apps.abit.synchronization.BitmessageService; import ch.dissem.bitmessage.entity.BitmessageAddress; +import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_ADD_CONTACT; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE; +import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT; + public class OpenBitmessageLinkActivity extends AppCompatActivity { + private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class); + + private Messenger service; + private boolean bound; + private ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + OpenBitmessageLinkActivity.this.service = new Messenger(service); + OpenBitmessageLinkActivity.this.bound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + service = null; + bound = false; + } + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -70,14 +103,29 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity { ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - BitmessageContext bmc = Singleton.getBitmessageContext(OpenBitmessageLinkActivity.this); BitmessageAddress bmAddress = new BitmessageAddress(address); bmAddress.setAlias(label.getText().toString()); - if (subscribe.isChecked()) { - bmc.addSubscribtion(bmAddress); - } - if (importContact.isChecked()) { - bmc.addContact(bmAddress); + + final int what; + if (subscribe.isChecked() && importContact.isChecked()) + what = MSG_SUBSCRIBE_AND_ADD_CONTACT; + else if (subscribe.isChecked()) + what = MSG_SUBSCRIBE; + else if (importContact.isChecked()) + what = MSG_ADD_CONTACT; + else + what = 0; + + if (what != 0) { + try { + Message message = Message.obtain(null, what); + Bundle bundle = new Bundle(); + bundle.putSerializable(DATA_FIELD_ADDRESS, bmAddress); + message.setData(bundle); + service.send(message); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } setResult(Activity.RESULT_OK); finish(); @@ -110,4 +158,19 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity { return new String[0]; } } + + @Override + protected void onStart() { + super.onStart(); + bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + if (bound) { + unbindService(connection); + bound = false; + } + super.onStop(); + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java index fa16aec..0fa31e8 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java @@ -27,6 +27,7 @@ import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; + import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; @@ -115,7 +116,7 @@ public class SubscriptionDetailFragment extends Fragment { @Override public void onPause() { - Singleton.getBitmessageContext(getActivity()).addresses().save(item); + Singleton.getAddressRepository(getContext()).save(item); super.onPause(); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java b/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java index a3c7b00..18fa320 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java @@ -24,6 +24,8 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; + +import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.valueobject.Label; @@ -32,7 +34,7 @@ import java.util.Comparator; import java.util.List; /** - * Created by chris on 06.09.15. + * Fragment that shows a list of all contacts, the ones we subscribed to first. */ public class SubscriptionListFragment extends AbstractItemListFragment<BitmessageAddress> { @Override @@ -43,7 +45,7 @@ public class SubscriptionListFragment extends AbstractItemListFragment<Bitmessag } public void updateList() { - List<BitmessageAddress> addresses = bmc.addresses().getContacts(); + List<BitmessageAddress> addresses = Singleton.getAddressRepository(getContext()).getContacts(); Collections.sort(addresses, new Comparator<BitmessageAddress>() { /** * Yields the following order: diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java index a013602..3530c2a 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java @@ -25,9 +25,9 @@ public class NetworkNotification extends AbstractNotification { private final BitmessageContext bmc; private NotificationCompat.Builder builder; - public NetworkNotification(Context ctx) { - super(ctx); - bmc = Singleton.getBitmessageContext(ctx); + public NetworkNotification(Context ctx, BitmessageContext bmc) { + super(ctx.getApplicationContext()); + this.bmc = bmc; builder = new NotificationCompat.Builder(ctx); builder.setSmallIcon(R.drawable.ic_notification_full_node) .setContentTitle(ctx.getString(R.string.bitmessage_full_node)) diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java index ee10bf1..2bb63df 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.Plaintext; import static ch.dissem.apps.abit.util.Drawables.toBitmap; public class NewMessageNotification extends AbstractNotification { + public static final int NEW_MESSAGE_NOTIFICATION_ID = 1; private static final StyleSpan SPAN_EMPHASIS = new StyleSpan(Typeface.BOLD); public NewMessageNotification(Context ctx) { @@ -76,6 +77,6 @@ public class NewMessageNotification extends AbstractNotification { @Override protected int getNotificationId() { - return 1; + return NEW_MESSAGE_NOTIFICATION_ID; } } diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java index eba1b0d..83293e7 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java @@ -23,11 +23,13 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; +import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.utils.Encode; @@ -45,7 +47,7 @@ import static ch.dissem.apps.abit.repository.SqlHelper.join; /** * {@link MessageRepository} implementation using the Android SQL API. */ -public class AndroidMessageRepository implements MessageRepository, InternalContext.ContextHolder { +public class AndroidMessageRepository implements MessageRepository { private static final Logger LOG = LoggerFactory.getLogger(AndroidMessageRepository.class); private static final String TABLE_NAME = "Message"; @@ -71,16 +73,13 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont private static final String LBL_COLUMN_ORDER = "ord"; private final SqlHelper sql; private final Context ctx; - private InternalContext bmc; + + private final AddressRepository addressRepo; public AndroidMessageRepository(SqlHelper sql, Context ctx) { this.sql = sql; this.ctx = ctx; - } - - @Override - public void setContext(InternalContext context) { - bmc = context; + this.addressRepo = Singleton.getAddressRepository(ctx); } @Override @@ -230,8 +229,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont long id = c.getLong(c.getColumnIndex(COLUMN_ID)); builder.id(id); builder.IV(new InventoryVector(iv)); - builder.from(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); - builder.to(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); + builder.from(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); + builder.to(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT))); builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED))); builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS)))); @@ -257,12 +256,12 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont // save from address if necessary if (message.getId() == null) { - BitmessageAddress savedAddress = bmc.getAddressRepo().getAddress(message.getFrom().getAddress()); + BitmessageAddress savedAddress = addressRepo.getAddress(message.getFrom().getAddress()); if (savedAddress == null || savedAddress.getPrivateKey() == null) { if (savedAddress != null && savedAddress.getAlias() != null) { message.getFrom().setAlias(savedAddress.getAlias()); } - bmc.getAddressRepo().save(message.getFrom()); + addressRepo.save(message.getFrom()); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index 220dc36..469d1db 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -2,6 +2,7 @@ package ch.dissem.apps.abit.service; import android.content.Context; +import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidInventory; @@ -9,35 +10,24 @@ import ch.dissem.apps.abit.repository.AndroidMessageRepository; import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.networking.DefaultNetworkHandler; +import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MemoryNodeRegistry; +import ch.dissem.bitmessage.ports.MessageRepository; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.security.sc.SpongySecurity; /** * Provides singleton objects across the application. */ public class Singleton { - private static BitmessageContext bitmessageContext; + private static SqlHelper sqlHelper; + private static Security security; + private static MessageRepository messageRepository; private static MessageListener messageListener; + private static AddressRepository addressRepository; - public static BitmessageContext getBitmessageContext(Context context) { - if (bitmessageContext == null) { - synchronized (Singleton.class) { - if (bitmessageContext == null) { - final Context ctx = context.getApplicationContext(); - SqlHelper sqlHelper = new SqlHelper(ctx); - bitmessageContext = new BitmessageContext.Builder() - .security(new SpongySecurity()) - .nodeRegistry(new MemoryNodeRegistry()) - .inventory(new AndroidInventory(sqlHelper)) - .addressRepo(new AndroidAddressRepository(sqlHelper)) - .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) - .networkHandler(new DefaultNetworkHandler()) - .listener(getMessageListener(ctx)) - .build(); - } - } - } - return bitmessageContext; + static { + ch.dissem.bitmessage.utils.Singleton.initialize(new SpongySecurity()); } public static MessageListener getMessageListener(Context ctx) { @@ -50,4 +40,41 @@ public class Singleton { } return messageListener; } + + public static SqlHelper getSqlHelper(Context ctx) { + if (sqlHelper == null) { + synchronized (Singleton.class) { + if (sqlHelper == null) { + sqlHelper = new SqlHelper(ctx.getApplicationContext()); + } + } + } + return sqlHelper; + } + + public static MessageRepository getMessageRepository(Context ctx) { + if (messageRepository == null) { + ctx = ctx.getApplicationContext(); + getSqlHelper(ctx); + synchronized (Singleton.class) { + if (messageRepository == null) { + messageRepository = new AndroidMessageRepository(sqlHelper, ctx); + } + } + } + return messageRepository; + } + + public static AddressRepository getAddressRepository(Context ctx) { + if (addressRepository == null) { + ctx = ctx.getApplicationContext(); + getSqlHelper(ctx); + synchronized (Singleton.class) { + if (addressRepository == null) { + addressRepository = new AndroidAddressRepository(sqlHelper); + } + } + } + return addressRepository; + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java new file mode 100644 index 0000000..76176fb --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java @@ -0,0 +1,186 @@ +package ch.dissem.apps.abit.synchronization; + +import android.app.Service; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +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.listener.MessageListener; +import ch.dissem.apps.abit.notification.NetworkNotification; +import ch.dissem.apps.abit.repository.AndroidInventory; +import ch.dissem.apps.abit.repository.SqlHelper; +import ch.dissem.apps.abit.service.Singleton; +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; +import ch.dissem.bitmessage.ports.MemoryNodeRegistry; +import ch.dissem.bitmessage.security.sc.SpongySecurity; + +import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; + +/** + * Define a Service that returns an IBinder for the + * sync adapter class, allowing the sync adapter framework to call + * onPerformSync(). + */ +public class BitmessageService extends Service { + public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class); + + public static final int MSG_SYNC = 2; + public static final int MSG_CREATE_IDENTITY = 10; + public static final int MSG_SUBSCRIBE = 20; + public static final int MSG_ADD_CONTACT = 21; + public static final int MSG_SUBSCRIBE_AND_ADD_CONTACT = 23; + public static final int MSG_START_NODE = 100; + public static final int MSG_STOP_NODE = 101; + + public static final String DATA_FIELD_ADDRESS = "address"; + + // Object to use as a thread-safe lock + private static final Object lock = new Object(); + + private static MessageListener messageListener = null; + private static NetworkNotification notification = null; + private static BitmessageContext bmc = null; + + private static volatile boolean running = false; + + private static Messenger messenger; + + public static boolean isRunning() { + return running; + } + + @Override + public void onCreate() { + synchronized (lock) { + if (bmc == null) { + messageListener = Singleton.getMessageListener(this); + SqlHelper sqlHelper = Singleton.getSqlHelper(this); + bmc = new BitmessageContext.Builder() + .security(new SpongySecurity()) + .nodeRegistry(new MemoryNodeRegistry()) + .inventory(new AndroidInventory(sqlHelper)) + .addressRepo(Singleton.getAddressRepository(this)) + .messageRepo(Singleton.getMessageRepository(this)) + .networkHandler(new DefaultNetworkHandler()) + .listener(messageListener) + .build(); + notification = new NetworkNotification(this, bmc); + messenger = new Messenger(new IncomingHandler()); + } + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return Service.START_STICKY; + } + + @Override + public void onDestroy() { + bmc.shutdown(); + running = false; + } + + /** + * Return an object that allows the system to invoke + * the sync adapter. + */ + @Override + public IBinder onBind(Intent intent) { + return messenger.getBinder(); + } + + private class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_CREATE_IDENTITY: + BitmessageAddress identity = bmc.createIdentity(false); + if (msg.replyTo != null) { + try { + Message message = Message.obtain(this, MSG_CREATE_IDENTITY); + Bundle bundle = new Bundle(); + bundle.putSerializable(DATA_FIELD_ADDRESS, identity); + message.setData(bundle); + msg.replyTo.send(message); + } catch (RemoteException e) { + LOG.debug(e.getMessage(), e); + } + } + break; + case MSG_SUBSCRIBE: + BitmessageAddress address = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); + bmc.addSubscribtion(address); + break; + case MSG_SYNC: + LOG.info("Synchronizing Bitmessage"); + // If the Bitmessage context acts as a full node, synchronization isn't necessary + if (bmc.isRunning()) break; + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences( + BitmessageService.this); + + String trustedNode = preferences.getString("trusted_node", null); + if (trustedNode == null) break; + trustedNode = trustedNode.trim(); + if (trustedNode.isEmpty()) break; + + 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); + } 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 + } + break; + case MSG_START_NODE: + startService(new Intent(BitmessageService.this, BitmessageService.class)); + // TODO: warn user, option to restrict to WiFi + running = true; + startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); + bmc.startup(); + notification.show(); + break; + case MSG_STOP_NODE: + bmc.shutdown(); + running = false; + stopForeground(false); + stopService(new Intent(BitmessageService.this, BitmessageService.class)); + break; + default: + super.handleMessage(msg); + } + } + } +} \ No newline at end of file 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 index f744299..dca34dc 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -1,16 +1,12 @@ package ch.dissem.apps.abit.synchronization; import android.app.Service; -import android.content.Context; import android.content.Intent; import android.os.IBinder; -import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.notification.NetworkNotification; -import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidInventory; -import ch.dissem.apps.abit.repository.AndroidMessageRepository; import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; @@ -26,20 +22,12 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF * onPerformSync(). */ public class SyncService extends Service { - private static MessageListener messageListener = null; - private static BitmessageContext bmc = null; // 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(); - private static volatile boolean running = false; - - public static boolean isRunning() { - return running; - } - - /* + /** * Instantiate the sync adapter object. */ @Override @@ -50,46 +38,12 @@ public class SyncService extends Service { * Disallow parallel syncs */ synchronized (syncAdapterLock) { - final Context ctx = getApplicationContext(); - if (bmc == null) { -// messageListener = new MessageListener(ctx); -// SqlHelper sqlHelper = new SqlHelper(ctx); -// bmc = new BitmessageContext.Builder() -// .security(new SpongySecurity()) -// .nodeRegistry(new MemoryNodeRegistry()) -// .inventory(new AndroidInventory(sqlHelper)) -// .addressRepo(new AndroidAddressRepository(sqlHelper)) -// .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) -// .networkHandler(new DefaultNetworkHandler()) -// .listener(messageListener) -// .build(); - // FIXME: this needs to change once I figured out how to get rid of those singletons - messageListener = Singleton.getMessageListener(ctx); - bmc = Singleton.getBitmessageContext(ctx); - } if (syncAdapter == null) { - syncAdapter = new SyncAdapter(ctx, bmc); + syncAdapter = new SyncAdapter(this, null); // FIXME } } } - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - // TODO: warn user, option to restrict to WiFi - running = true; - NetworkNotification networkNotification = new NetworkNotification(this); - startForeground(ONGOING_NOTIFICATION_ID, networkNotification.getNotification()); - bmc.startup(); - networkNotification.show(); - return Service.START_STICKY; - } - - @Override - public void onDestroy() { - bmc.shutdown(); - running = false; - } - /** * Return an object that allows the system to invoke * the sync adapter. From 725089c604b0a50d80f1755912c1bf3beea83a78 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 24 Oct 2015 20:59:24 +0200 Subject: [PATCH 08/12] Fixed NullPointerException and minor improvements --- .../dissem/apps/abit/MessageListActivity.java | 26 ++-- .../notification/NewMessageNotification.java | 1 - .../synchronization/BitmessageService.java | 2 +- .../res/layout/fragment_message_detail.xml | 130 +++++++++--------- 4 files changed, 80 insertions(+), 79 deletions(-) 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 3899a60..1797d17 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -280,25 +280,27 @@ public class MessageListActivity extends AppCompatActivity new SwitchDrawerItem() .withName(R.string.full_node) .withIcon(CommunityMaterial.Icon.cmd_cloud_outline) + .withChecked(BitmessageService.isRunning()) .withOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - try { - service.send(Message.obtain(null, MSG_START_NODE)); - } catch (RemoteException e) { - LOG.error(e.getMessage(), e); - } - } else { - try { - service.send(Message.obtain(null, MSG_STOP_NODE)); - } catch (RemoteException e) { - LOG.error(e.getMessage(), e); + if (messenger != null) { + if (isChecked) { + try { + service.send(Message.obtain(null, MSG_START_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } + } else { + try { + service.send(Message.obtain(null, MSG_STOP_NODE)); + } catch (RemoteException e) { + LOG.error(e.getMessage(), e); + } } } } }) - .withChecked(BitmessageService.isRunning()) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java index 2bb63df..4e66ab1 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java @@ -1,6 +1,5 @@ package ch.dissem.apps.abit.notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java index 76176fb..f611630 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java @@ -91,7 +91,7 @@ public class BitmessageService extends Service { @Override public void onDestroy() { - bmc.shutdown(); + if (bmc.isRunning()) bmc.shutdown(); running = false; } diff --git a/app/src/main/res/layout/fragment_message_detail.xml b/app/src/main/res/layout/fragment_message_detail.xml index b10bfab..55e0516 100644 --- a/app/src/main/res/layout/fragment_message_detail.xml +++ b/app/src/main/res/layout/fragment_message_detail.xml @@ -1,82 +1,82 @@ <?xml version="1.0" encoding="utf-8"?> -<ScrollView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_height="match_parent" - android:layout_width="match_parent"> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> <RelativeLayout - android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + android:orientation="vertical"> + + <TextView + android:id="@+id/subject" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> - - <TextView - android:id="@+id/subject" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" - android:gravity="center_vertical" - android:text="Subject" - android:padding="16dp" - android:layout_alignParentTop="true" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" - android:elegantTextHeight="false" - android:enabled="false"/> + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:elegantTextHeight="false" + android:enabled="false" + android:gravity="center_vertical" + android:padding="16dp" + android:text="Subject" + android:textAppearance="?android:attr/textAppearanceLarge" /> <View - android:id="@+id/divider" - android:layout_width="fill_parent" - android:layout_height="2dip" - android:layout_below="@id/subject" - android:background="@color/divider"/> + android:id="@+id/divider" + android:layout_width="fill_parent" + android:layout_height="2dip" + android:layout_below="@id/subject" + android:background="@color/divider" /> <ImageView - android:id="@+id/avatar" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_below="@+id/divider" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" - android:src="@color/accent" - android:layout_margin="16dp"/> + android:id="@+id/avatar" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_below="@+id/divider" + android:layout_margin="16dp" + android:src="@color/accent" /> <TextView - android:id="@+id/sender" - android:layout_width="wrap_content" - android:layout_height="20dp" - android:text="Sender" - android:layout_alignTop="@+id/avatar" - android:layout_toRightOf="@+id/avatar" - android:layout_toEndOf="@+id/avatar" - android:gravity="center_vertical" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:textStyle="bold"/> + android:id="@+id/sender" + android:layout_width="wrap_content" + android:layout_height="20dp" + android:layout_alignTop="@+id/avatar" + android:layout_toEndOf="@+id/avatar" + android:layout_toRightOf="@+id/avatar" + android:gravity="center_vertical" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:text="Sender" + android:textStyle="bold" /> <TextView - android:id="@+id/recipient" - android:layout_width="wrap_content" - android:layout_height="20dp" - android:text="Recipient" - android:gravity="center_vertical" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:layout_alignBottom="@+id/avatar" - android:layout_toRightOf="@+id/avatar" - android:layout_toEndOf="@+id/avatar"/> + android:id="@+id/recipient" + android:layout_width="wrap_content" + android:layout_height="20dp" + android:layout_alignBottom="@+id/avatar" + android:layout_toEndOf="@+id/avatar" + android:layout_toRightOf="@+id/avatar" + android:gravity="center_vertical" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:text="Recipient" /> <TextView - android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="New Text" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" - android:layout_marginTop="32dp" - android:paddingBottom="64dp" - android:layout_below="@+id/avatar" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true"/> + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_below="@+id/avatar" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginTop="32dp" + android:paddingBottom="64dp" + android:text="New Text" + android:textIsSelectable="true" /> </RelativeLayout> </ScrollView> \ No newline at end of file From 7fe7ee42fccfea19381e9ddaf203afeedc5ab3b7 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 25 Oct 2015 09:50:40 +0100 Subject: [PATCH 09/12] Fixed initialisation, added message/broadcast sending to service --- .../dissem/apps/abit/MessageListActivity.java | 29 ++++---- .../repository/AndroidMessageRepository.java | 21 +++--- .../dissem/apps/abit/service/Singleton.java | 62 +++++++---------- .../synchronization/BitmessageService.java | 66 ++++++++++++------- .../abit/synchronization/SyncAdapter.java | 6 +- .../abit/synchronization/SyncService.java | 2 +- 6 files changed, 97 insertions(+), 89 deletions(-) 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 1797d17..e3b469f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -47,14 +47,13 @@ import ch.dissem.apps.abit.listener.ListSelectionListener; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; import ch.dissem.apps.abit.synchronization.BitmessageService; -import ch.dissem.apps.abit.synchronization.SyncService; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; -import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS; +import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_IDENTITY; import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_START_NODE; import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_STOP_NODE; import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY; @@ -419,19 +418,23 @@ public class MessageListActivity extends AppCompatActivity @Override public void handleMessage(Message msg) { switch (msg.what) { - case BitmessageService.MSG_CREATE_IDENTITY: - BitmessageAddress identity = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); - IProfile newProfile = new ProfileDrawerItem() - .withName(identity.toString()) - .withEmail(identity.getAddress()) - .withTag(identity); - if (accountHeader.getProfiles() != null) { - //we know that there are 2 setting elements. set the new profile above them ;) - accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); - } else { - accountHeader.addProfiles(newProfile); + case BitmessageService.MSG_CREATE_IDENTITY: { + Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY); + if (data instanceof BitmessageAddress) { + BitmessageAddress identity = (BitmessageAddress) data; + IProfile newProfile = new ProfileDrawerItem() + .withName(identity.toString()) + .withEmail(identity.getAddress()) + .withTag(identity); + if (accountHeader.getProfiles() != null) { + //we know that there are 2 setting elements. set the new profile above them ;) + accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2); + } else { + accountHeader.addProfiles(newProfile); + } } break; + } default: super.handleMessage(msg); } diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java index 83293e7..eba1b0d 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java @@ -23,13 +23,11 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import ch.dissem.apps.abit.R; -import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; -import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.utils.Encode; @@ -47,7 +45,7 @@ import static ch.dissem.apps.abit.repository.SqlHelper.join; /** * {@link MessageRepository} implementation using the Android SQL API. */ -public class AndroidMessageRepository implements MessageRepository { +public class AndroidMessageRepository implements MessageRepository, InternalContext.ContextHolder { private static final Logger LOG = LoggerFactory.getLogger(AndroidMessageRepository.class); private static final String TABLE_NAME = "Message"; @@ -73,13 +71,16 @@ public class AndroidMessageRepository implements MessageRepository { private static final String LBL_COLUMN_ORDER = "ord"; private final SqlHelper sql; private final Context ctx; - - private final AddressRepository addressRepo; + private InternalContext bmc; public AndroidMessageRepository(SqlHelper sql, Context ctx) { this.sql = sql; this.ctx = ctx; - this.addressRepo = Singleton.getAddressRepository(ctx); + } + + @Override + public void setContext(InternalContext context) { + bmc = context; } @Override @@ -229,8 +230,8 @@ public class AndroidMessageRepository implements MessageRepository { long id = c.getLong(c.getColumnIndex(COLUMN_ID)); builder.id(id); builder.IV(new InventoryVector(iv)); - builder.from(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); - builder.to(addressRepo.getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); + builder.from(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER)))); + builder.to(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT)))); builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT))); builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED))); builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS)))); @@ -256,12 +257,12 @@ public class AndroidMessageRepository implements MessageRepository { // save from address if necessary if (message.getId() == null) { - BitmessageAddress savedAddress = addressRepo.getAddress(message.getFrom().getAddress()); + BitmessageAddress savedAddress = bmc.getAddressRepo().getAddress(message.getFrom().getAddress()); if (savedAddress == null || savedAddress.getPrivateKey() == null) { if (savedAddress != null && savedAddress.getAlias() != null) { message.getFrom().setAlias(savedAddress.getAlias()); } - addressRepo.save(message.getFrom()); + bmc.getAddressRepo().save(message.getFrom()); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index 469d1db..f00d3c7 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -2,6 +2,8 @@ package ch.dissem.apps.abit.service; import android.content.Context; +import java.util.Objects; + import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.repository.AndroidAddressRepository; @@ -20,14 +22,29 @@ import ch.dissem.bitmessage.security.sc.SpongySecurity; * Provides singleton objects across the application. */ public class Singleton { - private static SqlHelper sqlHelper; - private static Security security; - private static MessageRepository messageRepository; + public static final Object lock = new Object(); + private static BitmessageContext bitmessageContext; private static MessageListener messageListener; - private static AddressRepository addressRepository; - static { - ch.dissem.bitmessage.utils.Singleton.initialize(new SpongySecurity()); + public static BitmessageContext getBitmessageContext(Context context) { + if (bitmessageContext == null) { + synchronized (lock) { + if (bitmessageContext == null) { + final Context ctx = context.getApplicationContext(); + SqlHelper sqlHelper = new SqlHelper(ctx); + bitmessageContext = new BitmessageContext.Builder() + .security(new SpongySecurity()) + .nodeRegistry(new MemoryNodeRegistry()) + .inventory(new AndroidInventory(sqlHelper)) + .addressRepo(new AndroidAddressRepository(sqlHelper)) + .messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) + .networkHandler(new DefaultNetworkHandler()) + .listener(getMessageListener(ctx)) + .build(); + } + } + } + return bitmessageContext; } public static MessageListener getMessageListener(Context ctx) { @@ -41,40 +58,11 @@ public class Singleton { return messageListener; } - public static SqlHelper getSqlHelper(Context ctx) { - if (sqlHelper == null) { - synchronized (Singleton.class) { - if (sqlHelper == null) { - sqlHelper = new SqlHelper(ctx.getApplicationContext()); - } - } - } - return sqlHelper; - } - public static MessageRepository getMessageRepository(Context ctx) { - if (messageRepository == null) { - ctx = ctx.getApplicationContext(); - getSqlHelper(ctx); - synchronized (Singleton.class) { - if (messageRepository == null) { - messageRepository = new AndroidMessageRepository(sqlHelper, ctx); - } - } - } - return messageRepository; + return getBitmessageContext(ctx).messages(); } public static AddressRepository getAddressRepository(Context ctx) { - if (addressRepository == null) { - ctx = ctx.getApplicationContext(); - getSqlHelper(ctx); - synchronized (Singleton.class) { - if (addressRepository == null) { - addressRepository = new AndroidAddressRepository(sqlHelper); - } - } - } - return addressRepository; + return getBitmessageContext(ctx).addresses(); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java index f611630..bb15042 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java @@ -14,19 +14,14 @@ import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; -import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.notification.NetworkNotification; -import ch.dissem.apps.abit.repository.AndroidInventory; -import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.networking.DefaultNetworkHandler; -import ch.dissem.bitmessage.ports.MemoryNodeRegistry; -import ch.dissem.bitmessage.security.sc.SpongySecurity; import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; @@ -43,15 +38,19 @@ public class BitmessageService extends Service { public static final int MSG_SUBSCRIBE = 20; public static final int MSG_ADD_CONTACT = 21; public static final int MSG_SUBSCRIBE_AND_ADD_CONTACT = 23; + public static final int MSG_SEND_MESSAGE = 30; + public static final int MSG_SEND_BROADCAST = 31; public static final int MSG_START_NODE = 100; public static final int MSG_STOP_NODE = 101; + public static final String DATA_FIELD_IDENTITY = "identity"; public static final String DATA_FIELD_ADDRESS = "address"; + public static final String DATA_FIELD_SUBJECT = "subject"; + public static final String DATA_FIELD_MESSAGE = "message"; // Object to use as a thread-safe lock private static final Object lock = new Object(); - private static MessageListener messageListener = null; private static NetworkNotification notification = null; private static BitmessageContext bmc = null; @@ -67,17 +66,7 @@ public class BitmessageService extends Service { public void onCreate() { synchronized (lock) { if (bmc == null) { - messageListener = Singleton.getMessageListener(this); - SqlHelper sqlHelper = Singleton.getSqlHelper(this); - bmc = new BitmessageContext.Builder() - .security(new SpongySecurity()) - .nodeRegistry(new MemoryNodeRegistry()) - .inventory(new AndroidInventory(sqlHelper)) - .addressRepo(Singleton.getAddressRepository(this)) - .messageRepo(Singleton.getMessageRepository(this)) - .networkHandler(new DefaultNetworkHandler()) - .listener(messageListener) - .build(); + bmc = Singleton.getBitmessageContext(this); notification = new NetworkNotification(this, bmc); messenger = new Messenger(new IncomingHandler()); } @@ -108,13 +97,13 @@ public class BitmessageService extends Service { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_CREATE_IDENTITY: + case MSG_CREATE_IDENTITY: { BitmessageAddress identity = bmc.createIdentity(false); if (msg.replyTo != null) { try { Message message = Message.obtain(this, MSG_CREATE_IDENTITY); Bundle bundle = new Bundle(); - bundle.putSerializable(DATA_FIELD_ADDRESS, identity); + bundle.putSerializable(DATA_FIELD_IDENTITY, identity); message.setData(bundle); msg.replyTo.send(message); } catch (RemoteException e) { @@ -122,11 +111,15 @@ public class BitmessageService extends Service { } } break; - case MSG_SUBSCRIBE: - BitmessageAddress address = (BitmessageAddress) msg.getData().getSerializable(DATA_FIELD_ADDRESS); - bmc.addSubscribtion(address); + } + case MSG_SUBSCRIBE: { + Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS); + if (data instanceof BitmessageAddress) { + bmc.addSubscribtion((BitmessageAddress) data); + } break; - case MSG_SYNC: + } + case MSG_SYNC: { LOG.info("Synchronizing Bitmessage"); // If the Bitmessage context acts as a full node, synchronization isn't necessary if (bmc.isRunning()) break; @@ -164,9 +157,32 @@ public class BitmessageService extends Service { // TODO: show error as notification } break; + } + case MSG_SEND_MESSAGE: { + Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY); + Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS); + if (identity instanceof BitmessageAddress + && address instanceof BitmessageAddress) { + String subject = msg.getData().getString(DATA_FIELD_SUBJECT); + String message = msg.getData().getString(DATA_FIELD_MESSAGE); + bmc.send((BitmessageAddress) identity, (BitmessageAddress) address, + subject, message); + } + break; + } + case MSG_SEND_BROADCAST: { + Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY); + if (data instanceof BitmessageAddress) { + String subject = msg.getData().getString(DATA_FIELD_SUBJECT); + String message = msg.getData().getString(DATA_FIELD_MESSAGE); + bmc.broadcast((BitmessageAddress) data, subject, message); + } + break; + } case MSG_START_NODE: - startService(new Intent(BitmessageService.this, BitmessageService.class)); // TODO: warn user, option to restrict to WiFi + // (I'm not quite sure this can be done here, though) + startService(new Intent(BitmessageService.this, BitmessageService.class)); running = true; startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); bmc.startup(); 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 index efc1333..4935e68 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -30,9 +30,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Set up the sync adapter */ - public SyncAdapter(Context context, BitmessageContext bitmessageContext) { - super(context, true); - bmc = bitmessageContext; + public SyncAdapter(Context context, boolean autoInitialize) { + super(context, autoInitialize); + bmc = Singleton.getBitmessageContext(context); } @Override 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 index dca34dc..cdd0143 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -39,7 +39,7 @@ public class SyncService extends Service { */ synchronized (syncAdapterLock) { if (syncAdapter == null) { - syncAdapter = new SyncAdapter(this, null); // FIXME + syncAdapter = new SyncAdapter(this, true); } } } From 2a17bbe34b92e8fde6b80b4c54a7a87a51b01e2a Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 25 Oct 2015 11:29:46 +0100 Subject: [PATCH 10/12] Synchronization works, at least basically --- .../dissem/apps/abit/synchronization/Authenticator.java | 2 +- .../ch/dissem/apps/abit/synchronization/SyncAdapter.java | 9 ++++++--- .../ch/dissem/apps/abit/synchronization/SyncService.java | 4 ++++ app/src/main/res/xml/authenticator.xml | 2 +- app/src/main/res/xml/syncadapter.xml | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) 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 index 5a6d3c3..6d4eec5 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/Authenticator.java @@ -13,7 +13,7 @@ import android.os.Bundle; */ public class Authenticator extends AbstractAccountAuthenticator { public static final String ACCOUNT_NAME = "Bitmessage"; - public static final String ACCOUNT_TYPE = "bitmessage.dissem.ch"; + public static final String ACCOUNT_TYPE = "ch.dissem.bitmessage"; // Simple constructor public Authenticator(Context context) { 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 index 4935e68..4bbc627 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -37,9 +37,12 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { - LOG.info("Synchronizing Bitmessage"); // If the Bitmessage context acts as a full node, synchronization isn't necessary - if (bmc.isRunning()) return; + if (bmc.isRunning()) { + LOG.info("Synchronization skipped, Abit is acting as a full node"); + return; + } + LOG.info("Synchronizing Bitmessage"); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); @@ -63,7 +66,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { } else { port = 8444; } - long timeoutInSeconds = preferences.getInt("sync_timeout", 120); + long timeoutInSeconds = Long.parseLong(preferences.getString("sync_timeout", "120")); try { LOG.info("Synchronization started"); bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true); 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 index cdd0143..b3abcaf 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncService.java @@ -4,6 +4,9 @@ import android.app.Service; import android.content.Intent; import android.os.IBinder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.notification.NetworkNotification; import ch.dissem.apps.abit.repository.AndroidInventory; @@ -22,6 +25,7 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF * onPerformSync(). */ public class SyncService extends Service { + private static final Logger LOG = LoggerFactory.getLogger(SyncService.class); // Storage for an instance of the sync adapter private static SyncAdapter syncAdapter = null; // Object to use as a thread-safe lock diff --git a/app/src/main/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml index aa46bf6..17609a0 100644 --- a/app/src/main/res/xml/authenticator.xml +++ b/app/src/main/res/xml/authenticator.xml @@ -1,6 +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:accountType="ch.dissem.bitmessage" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:smallIcon="@mipmap/ic_launcher" /> \ No newline at end of file diff --git a/app/src/main/res/xml/syncadapter.xml b/app/src/main/res/xml/syncadapter.xml index 20cacd2..fabc86f 100644 --- a/app/src/main/res/xml/syncadapter.xml +++ b/app/src/main/res/xml/syncadapter.xml @@ -1,7 +1,7 @@ <?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:contentAuthority="ch.dissem.apps.abit.provider" android:accountType="ch.dissem.bitmessage" android:userVisible="true" android:supportsUploading="true" From e98eefe2cc3b8a220e89e9b7433d9539ea0e10e8 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 29 Oct 2015 16:38:50 +0100 Subject: [PATCH 11/12] Added notification for errors and warnings --- .../abit/notification/ErrorNotification.java | 41 +++++++++++++++++++ .../res/drawable/ic_notification_error.xml | 9 ++++ .../ic_notification_proof_of_work.xml | 9 ++++ .../res/drawable/ic_notification_warning.xml | 9 ++++ 4 files changed, 68 insertions(+) create mode 100644 app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java create mode 100644 app/src/main/res/drawable/ic_notification_error.xml create mode 100644 app/src/main/res/drawable/ic_notification_proof_of_work.xml create mode 100644 app/src/main/res/drawable/ic_notification_warning.xml diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java new file mode 100644 index 0000000..d14aa78 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java @@ -0,0 +1,41 @@ +package ch.dissem.apps.abit.notification; + +import android.content.Context; +import android.support.v7.app.NotificationCompat; + +import ch.dissem.apps.abit.R; + +/** + * Created by chrigu on 29.10.15. + */ +public class ErrorNotification extends AbstractNotification { + public static final int ERROR_NOTIFICATION_ID = 4; + + private NotificationCompat.Builder builder; + + public ErrorNotification(Context ctx) { + super(ctx); + builder = new NotificationCompat.Builder(ctx); + builder.setContentTitle(ctx.getString(R.string.app_name)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + } + + public ErrorNotification setWarning(int resId, Object... args) { + builder.setSmallIcon(R.drawable.ic_notification_warning) + .setContentText(ctx.getString(resId, args)); + notification = builder.build(); + return this; + } + + public ErrorNotification setError(int resId, Object... args) { + builder.setSmallIcon(R.drawable.ic_notification_error) + .setContentText(ctx.getString(resId, args)); + notification = builder.build(); + return this; + } + + @Override + protected int getNotificationId() { + return ERROR_NOTIFICATION_ID; + } +} diff --git a/app/src/main/res/drawable/ic_notification_error.xml b/app/src/main/res/drawable/ic_notification_error.xml new file mode 100644 index 0000000..bc132bb --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_error.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-2h2v2zm0,-4h-2V7h2v6z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_notification_proof_of_work.xml b/app/src/main/res/drawable/ic_notification_proof_of_work.xml new file mode 100644 index 0000000..c64f014 --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_proof_of_work.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M11.71,19C9.93,19 8.5,17.59 8.5,15.86C8.5,14.24 9.53,13.1 11.3,12.74C13.07,12.38 14.9,11.53 15.92,10.16C16.31,11.45 16.5,12.81 16.5,14.2C16.5,16.84 14.36,19 11.71,19M13.5,0.67C13.5,0.67 14.24,3.32 14.24,5.47C14.24,7.53 12.89,9.2 10.83,9.2C8.76,9.2 7.2,7.53 7.2,5.47L7.23,5.1C5.21,7.5 4,10.61 4,14A8,8 0 0,0 12,22A8,8 0 0,0 20,14C20,8.6 17.41,3.8 13.5,0.67Z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_notification_warning.xml b/app/src/main/res/drawable/ic_notification_warning.xml new file mode 100644 index 0000000..0510991 --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_warning.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/> +</vector> From 54a319638b9ede811aa8cf4bee1be8ff08d29baa Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 31 Oct 2015 07:49:03 +0100 Subject: [PATCH 12/12] Added a service based POW engine, so it shouldn't be killed by the system, at least not that easily. (WIP) --- app/src/main/AndroidManifest.xml | 3 +- .../dissem/apps/abit/MessageListActivity.java | 43 ++++++--- .../apps/abit/OpenBitmessageLinkActivity.java | 10 +-- .../notification/AbstractNotification.java | 6 +- .../abit/notification/ErrorNotification.java | 5 +- .../notification/NetworkNotification.java | 4 +- .../notification/ProofOfWorkNotification.java | 38 ++++++++ .../BitmessageService.java | 66 +++----------- .../apps/abit/service/ProofOfWorkService.java | 88 +++++++++++++++++++ .../apps/abit/service/ServicePowEngine.java | 61 +++++++++++++ .../dissem/apps/abit/service/Singleton.java | 5 +- .../abit/synchronization/SyncAdapter.java | 14 ++- app/src/main/res/values-de/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 14 files changed, 266 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java rename app/src/main/java/ch/dissem/apps/abit/{synchronization => service}/BitmessageService.java (67%) create mode 100644 app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 577db59..f0fbceb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -102,7 +102,8 @@ <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity> - <service android:name=".synchronization.BitmessageService" /> + <service android:name=".service.BitmessageService" /> + <service android:name=".service.ProofOfWorkService" /> <!-- Synchronization --> <provider 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 e3b469f..b8a359f 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListActivity.java @@ -40,22 +40,23 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.ArrayList; import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ListSelectionListener; +import ch.dissem.apps.abit.service.BitmessageService; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.synchronization.Authenticator; -import ch.dissem.apps.abit.synchronization.BitmessageService; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; -import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_IDENTITY; -import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_START_NODE; -import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_STOP_NODE; +import static ch.dissem.apps.abit.service.BitmessageService.DATA_FIELD_IDENTITY; +import static ch.dissem.apps.abit.service.BitmessageService.MSG_START_NODE; +import static ch.dissem.apps.abit.service.BitmessageService.MSG_STOP_NODE; import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY; @@ -82,7 +83,7 @@ public class MessageListActivity extends AppCompatActivity public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"; private static final Logger LOG = LoggerFactory.getLogger(MessageListActivity.class); - private static final long SYNC_FREQUENCY = 15;// FIXME * 60; // seconds + private static final long SYNC_FREQUENCY = 15 * 60; // seconds private static final int ADD_IDENTITY = 1; /** @@ -91,14 +92,15 @@ public class MessageListActivity extends AppCompatActivity */ private boolean twoPane; - private Messenger messenger = new Messenger(new IncomingHandler()); - private Messenger service; - private boolean bound; - private ServiceConnection connection = new ServiceConnection() { + private static IncomingHandler incomingHandler = new IncomingHandler(); + private static Messenger messenger = new Messenger(incomingHandler); + private static Messenger service; + private static boolean bound; + private static ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - MessageListActivity.this.service = new Messenger(service); - MessageListActivity.this.bound = true; + MessageListActivity.service = new Messenger(service); + MessageListActivity.bound = true; } @Override @@ -108,7 +110,6 @@ public class MessageListActivity extends AppCompatActivity } }; - private AccountHeader accountHeader; private Label selectedLabel; private MessageRepository messageRepo; @@ -208,7 +209,7 @@ public class MessageListActivity extends AppCompatActivity .withIcon(GoogleMaterial.Icon.gmd_settings) ); // Create the AccountHeader - accountHeader = new AccountHeaderBuilder() + AccountHeader accountHeader = new AccountHeaderBuilder() .withActivity(this) .withHeaderBackground(R.drawable.header) .withProfiles(profiles) @@ -229,6 +230,7 @@ public class MessageListActivity extends AppCompatActivity } }) .build(); + incomingHandler.updateAccountHeader(accountHeader); ArrayList<IDrawerItem> drawerItems = new ArrayList<>(); for (Label label : messageRepo.getLabels()) { @@ -414,11 +416,24 @@ public class MessageListActivity extends AppCompatActivity super.onStop(); } - private class IncomingHandler extends Handler { + private static class IncomingHandler extends Handler { + private WeakReference<AccountHeader> accountHeaderRef; + + private IncomingHandler() { + accountHeaderRef = new WeakReference<>(null); + } + + public void updateAccountHeader(AccountHeader accountHeader){ + accountHeaderRef = new WeakReference<>(accountHeader); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { case BitmessageService.MSG_CREATE_IDENTITY: { + AccountHeader accountHeader = accountHeaderRef.get(); + if (accountHeader == null) break; + Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY); if (data instanceof BitmessageAddress) { BitmessageAddress identity = (BitmessageAddress) data; diff --git a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java index 8f87f8a..17ffe41 100644 --- a/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/OpenBitmessageLinkActivity.java @@ -37,13 +37,13 @@ import android.widget.TextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ch.dissem.apps.abit.synchronization.BitmessageService; +import ch.dissem.apps.abit.service.BitmessageService; import ch.dissem.bitmessage.entity.BitmessageAddress; -import static ch.dissem.apps.abit.synchronization.BitmessageService.DATA_FIELD_ADDRESS; -import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_ADD_CONTACT; -import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE; -import static ch.dissem.apps.abit.synchronization.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT; +import static ch.dissem.apps.abit.service.BitmessageService.DATA_FIELD_ADDRESS; +import static ch.dissem.apps.abit.service.BitmessageService.MSG_ADD_CONTACT; +import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE; +import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT; public class OpenBitmessageLinkActivity extends AppCompatActivity { private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class); diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java index 60eb282..21ff505 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/AbstractNotification.java @@ -14,7 +14,7 @@ public abstract class AbstractNotification { public AbstractNotification(Context ctx) { - this.ctx = ctx; + this.ctx = ctx.getApplicationContext(); this.manager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); } @@ -23,6 +23,10 @@ public abstract class AbstractNotification { */ protected abstract int getNotificationId(); + public Notification getNotification() { + return notification; + } + public void show() { manager.notify(getNotificationId(), notification); } diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java index d14aa78..c64185e 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/ErrorNotification.java @@ -1,6 +1,7 @@ package ch.dissem.apps.abit.notification; import android.content.Context; +import android.support.annotation.StringRes; import android.support.v7.app.NotificationCompat; import ch.dissem.apps.abit.R; @@ -20,14 +21,14 @@ public class ErrorNotification extends AbstractNotification { .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); } - public ErrorNotification setWarning(int resId, Object... args) { + public ErrorNotification setWarning(@StringRes int resId, Object... args) { builder.setSmallIcon(R.drawable.ic_notification_warning) .setContentText(ctx.getString(resId, args)); notification = builder.build(); return this; } - public ErrorNotification setError(int resId, Object... args) { + public ErrorNotification setError(@StringRes int resId, Object... args) { builder.setSmallIcon(R.drawable.ic_notification_error) .setContentText(ctx.getString(resId, args)); notification = builder.build(); diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java index 3530c2a..bff6e92 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java @@ -12,7 +12,6 @@ import java.util.TimerTask; import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.R; -import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.utils.Property; @@ -26,7 +25,7 @@ public class NetworkNotification extends AbstractNotification { private NotificationCompat.Builder builder; public NetworkNotification(Context ctx, BitmessageContext bmc) { - super(ctx.getApplicationContext()); + super(ctx); this.bmc = bmc; builder = new NotificationCompat.Builder(ctx); builder.setSmallIcon(R.drawable.ic_notification_full_node) @@ -34,6 +33,7 @@ public class NetworkNotification extends AbstractNotification { .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); } + @Override public Notification getNotification() { update(); return notification; diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java new file mode 100644 index 0000000..f917a83 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/notification/ProofOfWorkNotification.java @@ -0,0 +1,38 @@ +package ch.dissem.apps.abit.notification; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.support.v7.app.NotificationCompat; + +import ch.dissem.apps.abit.MessageListActivity; +import ch.dissem.apps.abit.R; + +/** + * Ongoing notification while proof of work is in progress. + */ +public class ProofOfWorkNotification extends AbstractNotification { + public static final int ONGOING_NOTIFICATION_ID = 3; + + public ProofOfWorkNotification(Context ctx) { + super(ctx); + NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx); + + Intent showMessageIntent = new Intent(ctx, MessageListActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setUsesChronometer(true) + .setSmallIcon(R.drawable.ic_notification_proof_of_work) + .setContentTitle(ctx.getString(R.string.proof_of_work_title)) + .setContentText(ctx.getString(R.string.proof_of_work_text)) + .setContentIntent(pendingIntent); + + notification = builder.build(); + } + + @Override + protected int getNotificationId() { + return ONGOING_NOTIFICATION_ID; + } +} diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java similarity index 67% rename from app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java rename to app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java index bb15042..e52eb95 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/BitmessageService.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java @@ -1,25 +1,21 @@ -package ch.dissem.apps.abit.synchronization; +package ch.dissem.apps.abit.service; import android.app.Service; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; -import java.net.InetAddress; -import java.net.UnknownHostException; +import java.lang.ref.WeakReference; import ch.dissem.apps.abit.notification.NetworkNotification; -import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; @@ -33,7 +29,6 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF public class BitmessageService extends Service { public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class); - public static final int MSG_SYNC = 2; public static final int MSG_CREATE_IDENTITY = 10; public static final int MSG_SUBSCRIBE = 20; public static final int MSG_ADD_CONTACT = 21; @@ -68,7 +63,7 @@ public class BitmessageService extends Service { if (bmc == null) { bmc = Singleton.getBitmessageContext(this); notification = new NetworkNotification(this, bmc); - messenger = new Messenger(new IncomingHandler()); + messenger = new Messenger(new IncomingHandler(this)); } } } @@ -93,7 +88,13 @@ public class BitmessageService extends Service { return messenger.getBinder(); } - private class IncomingHandler extends Handler { + private static class IncomingHandler extends Handler { + private WeakReference<BitmessageService> service; + + private IncomingHandler(BitmessageService service) { + this.service = new WeakReference<>(service); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -119,45 +120,6 @@ public class BitmessageService extends Service { } break; } - case MSG_SYNC: { - LOG.info("Synchronizing Bitmessage"); - // If the Bitmessage context acts as a full node, synchronization isn't necessary - if (bmc.isRunning()) break; - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences( - BitmessageService.this); - - String trustedNode = preferences.getString("trusted_node", null); - if (trustedNode == null) break; - trustedNode = trustedNode.trim(); - if (trustedNode.isEmpty()) break; - - 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); - } 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 - } - break; - } case MSG_SEND_MESSAGE: { Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY); Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS); @@ -182,17 +144,17 @@ public class BitmessageService extends Service { case MSG_START_NODE: // TODO: warn user, option to restrict to WiFi // (I'm not quite sure this can be done here, though) - startService(new Intent(BitmessageService.this, BitmessageService.class)); + service.get().startService(new Intent(service.get(), BitmessageService.class)); running = true; - startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); + service.get().startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); bmc.startup(); notification.show(); break; case MSG_STOP_NODE: bmc.shutdown(); running = false; - stopForeground(false); - stopService(new Intent(BitmessageService.this, BitmessageService.class)); + service.get().stopForeground(false); + service.get().stopSelf(); break; default: super.handleMessage(msg); diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java new file mode 100644 index 0000000..7d86ef8 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/service/ProofOfWorkService.java @@ -0,0 +1,88 @@ +package ch.dissem.apps.abit.service; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.ref.WeakReference; + +import ch.dissem.apps.abit.notification.ProofOfWorkNotification; +import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; +import ch.dissem.bitmessage.ports.ProofOfWorkEngine; + +import static ch.dissem.apps.abit.notification.ProofOfWorkNotification.ONGOING_NOTIFICATION_ID; + +/** + * The Proof of Work Service makes sure POW is done in a foreground process, so it shouldn't be + * killed by the system before the nonce is found. + */ +public class ProofOfWorkService extends Service { + public static final Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class); + + // Object to use as a thread-safe lock + private static final Object lock = new Object(); + private static ProofOfWorkEngine engine; + + @Override + public void onCreate() { + synchronized (lock) { + if (engine == null) { + engine = new MultiThreadedPOWEngine(); + } + } + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return new PowBinder(engine, this); + } + + public static class PowBinder extends Binder { + private final ProofOfWorkEngine engine; + + private PowBinder(ProofOfWorkEngine engine, ProofOfWorkService service) { + this.engine = new EngineWrapper(engine, service); + } + + public ProofOfWorkEngine getEngine() { + return engine; + } + } + + private static class EngineWrapper implements ProofOfWorkEngine { + private final ProofOfWorkNotification notification; + private final ProofOfWorkEngine engine; + private final WeakReference<ProofOfWorkService> serviceRef; + + private EngineWrapper(ProofOfWorkEngine engine, ProofOfWorkService service) { + this.engine = engine; + this.serviceRef = new WeakReference<>(service); + this.notification = new ProofOfWorkNotification(service); + } + + @Override + public void calculateNonce(byte[] initialHash, byte[] target, final Callback callback) { + final ProofOfWorkService service = serviceRef.get(); + service.startService(new Intent(service, ProofOfWorkService.class)); + service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); + engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + try { + callback.onNonceCalculated(nonce); + } finally { + service.stopForeground(true); + service.stopSelf(); + } + } + }); + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java b/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java new file mode 100644 index 0000000..16d2aaa --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/service/ServicePowEngine.java @@ -0,0 +1,61 @@ +package ch.dissem.apps.abit.service; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; + +import java.util.concurrent.Semaphore; + +import ch.dissem.apps.abit.service.ProofOfWorkService.PowBinder; +import ch.dissem.bitmessage.ports.ProofOfWorkEngine; + +import static android.content.Context.BIND_AUTO_CREATE; + +/** + * Proof of Work engine that uses the Proof of Work service. + */ +public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Callback { + private final Semaphore semaphore = new Semaphore(1, true); + private final Context ctx; + + private byte[] initialHash, targetValue; + private Callback callback; + + public ServicePowEngine(Context ctx) { + this.ctx = ctx; + } + + private ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + ((PowBinder) service).getEngine().calculateNonce(initialHash, targetValue, ServicePowEngine.this); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + semaphore.release(); + } + }; + + @Override + public void calculateNonce(byte[] initialHash, byte[] targetValue, Callback callback) { + try { + semaphore.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + this.initialHash = initialHash; + this.targetValue = targetValue; + this.callback = callback; + ctx.bindService(new Intent(ctx, ProofOfWorkService.class), connection, BIND_AUTO_CREATE); + } + + @Override + public void onNonceCalculated(byte[] bytes) { + callback.onNonceCalculated(bytes); + ctx.unbindService(connection); + } + +} diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index f00d3c7..a178519 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -2,9 +2,6 @@ package ch.dissem.apps.abit.service; import android.content.Context; -import java.util.Objects; - -import ch.dissem.apps.abit.MessageListActivity; import ch.dissem.apps.abit.listener.MessageListener; import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidInventory; @@ -15,7 +12,6 @@ import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.ports.MessageRepository; -import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.security.sc.SpongySecurity; /** @@ -33,6 +29,7 @@ public class Singleton { final Context ctx = context.getApplicationContext(); SqlHelper sqlHelper = new SqlHelper(ctx); bitmessageContext = new BitmessageContext.Builder() + .proofOfWorkEngine(new ServicePowEngine(ctx)) .security(new SpongySecurity()) .nodeRegistry(new MemoryNodeRegistry()) .inventory(new AndroidInventory(sqlHelper)) 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 index 4bbc627..7654833 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.UnknownHostException; +import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.notification.ErrorNotification; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.BitmessageContext; @@ -59,8 +61,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { try { port = Integer.parseInt(portString); } catch (NumberFormatException e) { - LOG.error("Invalid port " + portString); - // TODO: show error as notification + new ErrorNotification(getContext()) + .setError(R.string.error_invalid_sync_port, portString) + .show(); return; } } else { @@ -72,8 +75,11 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { 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 + new ErrorNotification(getContext()) + .setError(R.string.error_invalid_sync_host) + .show(); + } catch (RuntimeException e) { + LOG.error(e.getMessage(), e); } } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 383adde..7020bf7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -42,4 +42,8 @@ <string name="connection_info_n">Stream %1$d: %2$d Verbindungen</string> <string name="connection_info_disconnected">Getrennt</string> <string name="connection_info_pending">Verbindung wird aufgebaut…</string> + <string name="proof_of_work_text">Warnung: dies könnte das Gerät erwärmen bis die Batterie leer ist.</string> + <string name="proof_of_work_title">Proof of Work</string> + <string name="error_invalid_sync_host">Synchronisation fehlgeschlagen: der vertrauenswürdige Knoten konnte nicht erreicht werden.</string> + <string name="error_invalid_sync_port">Ungültiger Port in den Synchronisationseinstellungen: %s</string> </resources> \ 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 b493dc2..ed1cd40 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,4 +42,8 @@ <string name="send">Send</string> <string name="connection_info_disconnected">Disconnected</string> <string name="connection_info_pending">Connecting…</string> + <string name="proof_of_work_title">Proof of Work</string> + <string name="proof_of_work_text">Warning: This might heat your device until the battery\'s dead.</string> + <string name="error_invalid_sync_port">Invalid port in synchronization settings: %s</string> + <string name="error_invalid_sync_host">Synchronization failed: Trusted node could not be reached.</string> </resources>