Merge branch 'feature/SyncAdapter' into develop
@ -1,104 +1,133 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="ch.dissem.apps.abit">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="ch.dissem.apps.abit">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MessageListActivity"
|
||||
android:label="@string/app_name">
|
||||
android:name=".MessageListActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MessageDetailActivity"
|
||||
android:label="@string/title_message_detail"
|
||||
android:parentActivityName=".MessageListActivity"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:name=".MessageDetailActivity"
|
||||
android:label="@string/title_message_detail"
|
||||
android:parentActivityName=".MessageListActivity"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity"/>
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SubscriptionDetailActivity"
|
||||
android:label="@string/title_subscription_detail"
|
||||
android:parentActivityName=".MessageListActivity"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:name=".SubscriptionDetailActivity"
|
||||
android:label="@string/title_subscription_detail"
|
||||
android:parentActivityName=".MessageListActivity"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity"/>
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ComposeMessageActivity"
|
||||
android:label="Compose"
|
||||
android:parentActivityName=".MessageListActivity">
|
||||
android:name=".ComposeMessageActivity"
|
||||
android:label="Compose"
|
||||
android:parentActivityName=".MessageListActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity"/>
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MessageListActivity" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SENDTO"/>
|
||||
<action android:name="android.intent.action.SENDTO" />
|
||||
|
||||
<data android:scheme="bitmessage"/>
|
||||
<data android:scheme="bitmsg"/>
|
||||
<data android:scheme="bm"/>
|
||||
<data android:scheme="bitmessage" />
|
||||
<data android:scheme="bitmsg" />
|
||||
<data android:scheme="bm" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<data android:mimeType="text/plain"/>
|
||||
<data android:mimeType="text/plain" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
|
||||
<data android:mimeType="text/plain"/>
|
||||
<data android:mimeType="text/plain" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName=".MessageListActivity">
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName=".MessageListActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
|
||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".OpenBitmessageLinkActivity"
|
||||
android:label="@string/title_activity_open_bitmessage_link"
|
||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||
android:name=".OpenBitmessageLinkActivity"
|
||||
android:label="@string/title_activity_open_bitmessage_link"
|
||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:scheme="bitmessage"/>
|
||||
<data android:scheme="bitmsg"/>
|
||||
<data android:scheme="bm"/>
|
||||
<data android:scheme="bitmessage" />
|
||||
<data android:scheme="bitmsg" />
|
||||
<data android:scheme="bm" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".service.BitmessageService" />
|
||||
<service android:name=".service.ProofOfWorkService" />
|
||||
|
||||
<!-- Synchronization -->
|
||||
<provider
|
||||
android:name=".synchronization.StubProvider"
|
||||
android:authorities="ch.dissem.apps.abit.provider"
|
||||
android:exported="false"
|
||||
android:syncable="true" />
|
||||
<service android:name=".synchronization.AuthenticatorService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".synchronization.SyncService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/syncadapter" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -17,13 +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;
|
||||
|
||||
/**
|
||||
@ -44,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.
|
||||
@ -54,16 +53,10 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
|
||||
* The current activated item position. Only used on tablets.
|
||||
*/
|
||||
private int activatedPosition = ListView.INVALID_POSITION;
|
||||
private boolean activateOnItemClick;
|
||||
|
||||
abstract void updateList(Label label);
|
||||
|
||||
@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);
|
||||
@ -76,15 +69,26 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
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(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
|
||||
@ -118,11 +122,15 @@ public abstract class AbstractItemListFragment<T> 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) {
|
||||
|
@ -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;
|
||||
|
@ -1,23 +1,24 @@
|
||||
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;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
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 android.widget.CompoundButton;
|
||||
|
||||
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||
@ -29,18 +30,34 @@ 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
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.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.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;
|
||||
|
||||
|
||||
/**
|
||||
@ -66,6 +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 * 60; // seconds
|
||||
private static final int ADD_IDENTITY = 1;
|
||||
|
||||
/**
|
||||
@ -74,16 +92,36 @@ public class MessageListActivity extends AppCompatActivity
|
||||
*/
|
||||
private boolean twoPane;
|
||||
|
||||
private AccountHeader accountHeader;
|
||||
private BitmessageContext bmc;
|
||||
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.service = new Messenger(service);
|
||||
MessageListActivity.bound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
service = null;
|
||||
bound = false;
|
||||
}
|
||||
};
|
||||
|
||||
private Label selectedLabel;
|
||||
private Menu menu;
|
||||
|
||||
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);
|
||||
|
||||
@ -99,6 +137,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);
|
||||
@ -109,16 +151,22 @@ public class MessageListActivity extends AppCompatActivity
|
||||
if (getIntent().hasExtra(EXTRA_SHOW_MESSAGE)) {
|
||||
onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE));
|
||||
}
|
||||
|
||||
createSyncAccount();
|
||||
}
|
||||
|
||||
@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 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,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))
|
||||
@ -161,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)
|
||||
@ -169,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
|
||||
@ -186,9 +230,10 @@ public class MessageListActivity extends AppCompatActivity
|
||||
}
|
||||
})
|
||||
.build();
|
||||
incomingHandler.updateAccountHeader(accountHeader);
|
||||
|
||||
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:
|
||||
@ -227,26 +272,43 @@ 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)
|
||||
.withChecked(BitmessageService.isRunning())
|
||||
.withOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
|
||||
@Override
|
||||
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();
|
||||
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;
|
||||
@ -262,6 +324,12 @@ 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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -271,34 +339,14 @@ 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();
|
||||
new NetworkNotification(this).show();
|
||||
updateMenu();
|
||||
return true;
|
||||
case R.id.sync_enabled:
|
||||
bmc.shutdown();
|
||||
updateMenu();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
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();
|
||||
changeList(listFragment);
|
||||
listFragment.updateList(selectedLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,6 +393,7 @@ public class MessageListActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void updateTitle(CharSequence title) {
|
||||
//noinspection ConstantConditions
|
||||
getSupportActionBar().setTitle(title);
|
||||
}
|
||||
|
||||
@ -352,4 +401,58 @@ 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 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
package ch.dissem.apps.abit;
|
||||
|
||||
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.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.TextView;
|
||||
|
||||
import ch.dissem.apps.abit.listener.ActionBarListener;
|
||||
import ch.dissem.apps.abit.listener.ListSelectionListener;
|
||||
import ch.dissem.apps.abit.service.Singleton;
|
||||
import ch.dissem.bitmessage.entity.Plaintext;
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||
@ -52,11 +61,14 @@ 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,
|
||||
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) {
|
||||
@ -81,7 +93,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);
|
||||
@ -117,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);
|
||||
}
|
||||
|
@ -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.service.BitmessageService;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -10,11 +10,11 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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(@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(@StringRes 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -11,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;
|
||||
|
||||
@ -19,18 +19,26 @@ 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;
|
||||
|
||||
public NetworkNotification(Context ctx) {
|
||||
public NetworkNotification(Context ctx, BitmessageContext bmc) {
|
||||
super(ctx);
|
||||
bmc = Singleton.getBitmessageContext(ctx);
|
||||
this.bmc = bmc;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -21,6 +20,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 +76,6 @@ public class NewMessageNotification extends AbstractNotification {
|
||||
|
||||
@Override
|
||||
protected int getNotificationId() {
|
||||
return 1;
|
||||
return NEW_MESSAGE_NOTIFICATION_ID;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -0,0 +1,164 @@
|
||||
package ch.dissem.apps.abit.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ch.dissem.apps.abit.notification.NetworkNotification;
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
|
||||
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_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_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 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) {
|
||||
bmc = Singleton.getBitmessageContext(this);
|
||||
notification = new NetworkNotification(this, bmc);
|
||||
messenger = new Messenger(new IncomingHandler(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (bmc.isRunning()) 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 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) {
|
||||
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_IDENTITY, identity);
|
||||
message.setData(bundle);
|
||||
msg.replyTo.send(message);
|
||||
} catch (RemoteException e) {
|
||||
LOG.debug(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_SUBSCRIBE: {
|
||||
Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
||||
if (data instanceof BitmessageAddress) {
|
||||
bmc.addSubscribtion((BitmessageAddress) data);
|
||||
}
|
||||
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:
|
||||
// TODO: warn user, option to restrict to WiFi
|
||||
// (I'm not quite sure this can be done here, though)
|
||||
service.get().startService(new Intent(service.get(), BitmessageService.class));
|
||||
running = true;
|
||||
service.get().startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
|
||||
bmc.startup();
|
||||
notification.show();
|
||||
break;
|
||||
case MSG_STOP_NODE:
|
||||
bmc.shutdown();
|
||||
running = false;
|
||||
service.get().stopForeground(false);
|
||||
service.get().stopSelf();
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -9,23 +9,27 @@ 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.security.sc.SpongySecurity;
|
||||
|
||||
/**
|
||||
* Provides singleton objects across the application.
|
||||
*/
|
||||
public class Singleton {
|
||||
public static final Object lock = new Object();
|
||||
private static BitmessageContext bitmessageContext;
|
||||
private static MessageListener messageListener;
|
||||
|
||||
public static BitmessageContext getBitmessageContext(Context context) {
|
||||
if (bitmessageContext == null) {
|
||||
synchronized (Singleton.class) {
|
||||
synchronized (lock) {
|
||||
if (bitmessageContext == null) {
|
||||
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))
|
||||
@ -50,4 +54,12 @@ public class Singleton {
|
||||
}
|
||||
return messageListener;
|
||||
}
|
||||
|
||||
public static MessageRepository getMessageRepository(Context ctx) {
|
||||
return getBitmessageContext(ctx).messages();
|
||||
}
|
||||
|
||||
public static AddressRepository getAddressRepository(Context ctx) {
|
||||
return getBitmessageContext(ctx).addresses();
|
||||
}
|
||||
}
|
||||
|
@ -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 = "ch.dissem.bitmessage";
|
||||
|
||||
// Simple constructor
|
||||
public Authenticator(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
// Editing properties is not supported
|
||||
@Override
|
||||
public Bundle editProperties(
|
||||
AccountAuthenticatorResponse r, String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Don't add additional accounts
|
||||
@Override
|
||||
public Bundle addAccount(
|
||||
AccountAuthenticatorResponse r,
|
||||
String s,
|
||||
String s2,
|
||||
String[] strings,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ignore attempts to confirm credentials
|
||||
@Override
|
||||
public Bundle confirmCredentials(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Getting an authentication token is not supported
|
||||
@Override
|
||||
public Bundle getAuthToken(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
String s,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Getting a label for the auth token is not supported
|
||||
@Override
|
||||
public String getAuthTokenLabel(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Updating user credentials is not supported
|
||||
@Override
|
||||
public Bundle updateCredentials(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
String s, Bundle bundle) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Checking features for the account is not supported
|
||||
@Override
|
||||
public Bundle hasFeatures(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account, String[] strings) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ch.dissem.apps.abit.synchronization;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* A bound Service that instantiates the authenticator
|
||||
* when started.
|
||||
*/
|
||||
public class AuthenticatorService extends Service {
|
||||
/**
|
||||
* Instance field that stores the authenticator object
|
||||
*/
|
||||
private Authenticator authenticator;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Create a new authenticator object
|
||||
authenticator = new Authenticator(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the system binds to this Service to make the RPC call
|
||||
* return the authenticator's IBinder.
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return authenticator.getIBinder();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
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 {
|
||||
public static final String AUTHORITY = "ch.dissem.apps.abit.provider";
|
||||
|
||||
/*
|
||||
* Always return true, indicating that the
|
||||
* provider loaded correctly.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return no type for MIME type
|
||||
*/
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* query() always returns no results
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(
|
||||
Uri uri,
|
||||
String[] projection,
|
||||
String selection,
|
||||
String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* insert() always returns null (no URI)
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete() always returns "no rows affected" (0)
|
||||
*/
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* update() always returns "no rows affected" (0)
|
||||
*/
|
||||
public int update(
|
||||
Uri uri,
|
||||
ContentValues values,
|
||||
String selection,
|
||||
String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
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.R;
|
||||
import ch.dissem.apps.abit.notification.ErrorNotification;
|
||||
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);
|
||||
|
||||
private final BitmessageContext bmc;
|
||||
|
||||
/**
|
||||
* Set up the sync adapter
|
||||
*/
|
||||
public SyncAdapter(Context context, boolean autoInitialize) {
|
||||
super(context, autoInitialize);
|
||||
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()) {
|
||||
LOG.info("Synchronization skipped, Abit is acting as a full node");
|
||||
return;
|
||||
}
|
||||
LOG.info("Synchronizing Bitmessage");
|
||||
|
||||
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);
|
||||
} catch (NumberFormatException e) {
|
||||
new ErrorNotification(getContext())
|
||||
.setError(R.string.error_invalid_sync_port, portString)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
port = 8444;
|
||||
}
|
||||
long timeoutInSeconds = Long.parseLong(preferences.getString("sync_timeout", "120"));
|
||||
try {
|
||||
LOG.info("Synchronization started");
|
||||
bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
|
||||
LOG.info("Synchronization finished");
|
||||
} catch (UnknownHostException e) {
|
||||
new ErrorNotification(getContext())
|
||||
.setError(R.string.error_invalid_sync_host)
|
||||
.show();
|
||||
} catch (RuntimeException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package ch.dissem.apps.abit.synchronization;
|
||||
|
||||
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;
|
||||
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 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
|
||||
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(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 580 B |
Before Width: | Height: | Size: 641 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.8 KiB |
9
app/src/main/res/drawable/ic_notification_error.xml
Normal file
@ -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>
|
@ -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>
|
9
app/src/main/res/drawable/ic_notification_warning.xml
Normal file
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -4,9 +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="disable_sync">Synchronisieren ausschalten</string>
|
||||
<string name="enable_sync">Synchronisieren einschalten</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>
|
||||
@ -33,8 +31,19 @@
|
||||
<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>
|
||||
<string name="full_node">Aktiver Knoten</string>
|
||||
<string name="send">Senden</string>
|
||||
<string name="write_message">Schreiben</string>
|
||||
<string name="connection_info_1">Stream %1$d: eine Verbindung</string>
|
||||
<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>
|
@ -2,9 +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="disable_sync">Disable Sync</string>
|
||||
<string name="enable_sync">Enable Sync</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>
|
||||
@ -35,6 +33,17 @@
|
||||
<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>
|
||||
<string name="full_node">Full node</string>
|
||||
<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>
|
||||
|
6
app/src/main/res/xml/authenticator.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="ch.dissem.bitmessage"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:smallIcon="@mipmap/ic_launcher" />
|
@ -9,8 +9,19 @@
|
||||
android:entryValues="@array/connection_mode_values"/>
|
||||
-->
|
||||
<SwitchPreference
|
||||
android: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>
|
9
app/src/main/res/xml/syncadapter.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<sync-adapter
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="ch.dissem.apps.abit.provider"
|
||||
android:accountType="ch.dissem.bitmessage"
|
||||
android:userVisible="true"
|
||||
android:supportsUploading="true"
|
||||
android:allowParallelSyncs="false"
|
||||
android:isAlwaysSyncable="true"/>
|