Server POW should work now
This commit is contained in:
parent
b0828ec1e5
commit
41f4571bf6
@ -29,6 +29,7 @@ dependencies {
|
|||||||
compile 'ch.dissem.jabit:jabit-domain:0.2.1-SNAPSHOT'
|
compile 'ch.dissem.jabit:jabit-domain:0.2.1-SNAPSHOT'
|
||||||
compile 'ch.dissem.jabit:jabit-networking:0.2.1-SNAPSHOT'
|
compile 'ch.dissem.jabit:jabit-networking:0.2.1-SNAPSHOT'
|
||||||
compile 'ch.dissem.jabit:jabit-security-spongy:0.2.1-SNAPSHOT'
|
compile 'ch.dissem.jabit:jabit-security-spongy:0.2.1-SNAPSHOT'
|
||||||
|
compile 'ch.dissem.jabit:jabit-extensions:0.2.1-SNAPSHOT'
|
||||||
|
|
||||||
compile 'org.slf4j:slf4j-android:1.7.12'
|
compile 'org.slf4j:slf4j-android:1.7.12'
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ CREATE TABLE Message (
|
|||||||
sent INTEGER,
|
sent INTEGER,
|
||||||
received INTEGER,
|
received INTEGER,
|
||||||
status VARCHAR(20) NOT NULL,
|
status VARCHAR(20) NOT NULL,
|
||||||
|
initial_hash BINARY(64) UNIQUE,
|
||||||
|
|
||||||
FOREIGN KEY (sender) REFERENCES Address (address),
|
FOREIGN KEY (sender) REFERENCES Address (address),
|
||||||
FOREIGN KEY (recipient) REFERENCES Address (address)
|
FOREIGN KEY (recipient) REFERENCES Address (address)
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
-- This is done in V1.2, as SQLite doesn't support ADD CONSTRAINT and a proper migration
|
||||||
|
-- wasn't really necessary yet.
|
||||||
|
--
|
||||||
|
-- This file is here to reduce confusion regarding to the original migration files.
|
||||||
|
|
||||||
|
--ALTER TABLE Message ADD COLUMN initial_hash BINARY(64);
|
||||||
|
--ALTER TABLE Message ADD CONSTRAINT initial_hash_unique UNIQUE(initial_hash);
|
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE POW (
|
||||||
|
initial_hash BINARY(64) PRIMARY KEY,
|
||||||
|
data BLOB NOT NULL,
|
||||||
|
version BIGINT NOT NULL,
|
||||||
|
nonce_trials_per_byte BIGINT NOT NULL,
|
||||||
|
extra_bytes BIGINT NOT NULL
|
||||||
|
);
|
@ -50,6 +50,8 @@ import ch.dissem.apps.abit.listener.ListSelectionListener;
|
|||||||
import ch.dissem.apps.abit.service.BitmessageService;
|
import ch.dissem.apps.abit.service.BitmessageService;
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.apps.abit.synchronization.Authenticator;
|
import ch.dissem.apps.abit.synchronization.Authenticator;
|
||||||
|
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
||||||
|
import ch.dissem.apps.abit.util.Preferences;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
@ -85,7 +87,6 @@ public class MainActivity extends AppCompatActivity
|
|||||||
public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox";
|
public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox";
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class);
|
||||||
private static final long SYNC_FREQUENCY = 15 * 60; // seconds
|
|
||||||
private static final int ADD_IDENTITY = 1;
|
private static final int ADD_IDENTITY = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +135,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
MessageListFragment listFragment = new MessageListFragment();
|
MessageListFragment listFragment = new MessageListFragment();
|
||||||
getSupportFragmentManager().beginTransaction().replace(R.id.item_list, listFragment).commit();
|
getSupportFragmentManager().beginTransaction().replace(R.id.item_list, listFragment)
|
||||||
|
.commit();
|
||||||
|
|
||||||
if (findViewById(R.id.message_detail_container) != null) {
|
if (findViewById(R.id.message_detail_container) != null) {
|
||||||
// The detail container view will be present only in the
|
// The detail container view will be present only in the
|
||||||
@ -157,21 +159,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE));
|
onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
createSyncAccount();
|
if (Preferences.useTrustedNode(this)) {
|
||||||
}
|
SyncAdapter.startSync(this);
|
||||||
|
} else {
|
||||||
private void createSyncAccount() {
|
SyncAdapter.stopSync(this);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,10 +211,12 @@ public class MainActivity extends AppCompatActivity
|
|||||||
.withProfiles(profiles)
|
.withProfiles(profiles)
|
||||||
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
|
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
|
public boolean onProfileChanged(View view, IProfile profile, boolean
|
||||||
|
currentProfile) {
|
||||||
if (profile.getIdentifier() == ADD_IDENTITY) {
|
if (profile.getIdentifier() == ADD_IDENTITY) {
|
||||||
try {
|
try {
|
||||||
Message message = Message.obtain(null, BitmessageService.MSG_CREATE_IDENTITY);
|
Message message = Message.obtain(null, BitmessageService
|
||||||
|
.MSG_CREATE_IDENTITY);
|
||||||
message.replyTo = messenger;
|
message.replyTo = messenger;
|
||||||
service.send(message);
|
service.send(message);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@ -240,14 +233,15 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
if (profiles.size() > 0) {
|
if (profiles.size() > 2) { // There's always the add and manage identity items
|
||||||
accountHeader.setActiveProfile(profiles.get(0), true);
|
accountHeader.setActiveProfile(profiles.get(0), true);
|
||||||
}
|
}
|
||||||
incomingHandler.updateAccountHeader(accountHeader);
|
incomingHandler.updateAccountHeader(accountHeader);
|
||||||
|
|
||||||
ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
|
ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
|
||||||
for (Label label : labels) {
|
for (Label label : labels) {
|
||||||
PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag(label);
|
PrimaryDrawerItem item = new PrimaryDrawerItem().withName(label.toString()).withTag
|
||||||
|
(label);
|
||||||
if (label.getType() == null) {
|
if (label.getType() == null) {
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_label);
|
item.withIcon(CommunityMaterial.Icon.cmd_label);
|
||||||
} else {
|
} else {
|
||||||
@ -301,17 +295,21 @@ public class MainActivity extends AppCompatActivity
|
|||||||
.withChecked(BitmessageService.isRunning())
|
.withChecked(BitmessageService.isRunning())
|
||||||
.withOnCheckedChangeListener(new OnCheckedChangeListener() {
|
.withOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
|
public void onCheckedChanged(IDrawerItem drawerItem,
|
||||||
|
CompoundButton buttonView,
|
||||||
|
boolean isChecked) {
|
||||||
if (messenger != null) {
|
if (messenger != null) {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
try {
|
try {
|
||||||
service.send(Message.obtain(null, MSG_START_NODE));
|
service.send(Message.obtain(null,
|
||||||
|
MSG_START_NODE));
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
service.send(Message.obtain(null, MSG_STOP_NODE));
|
service.send(Message.obtain(null,
|
||||||
|
MSG_STOP_NODE));
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@ -322,7 +320,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
)
|
)
|
||||||
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
|
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onItemClick(AdapterView<?> adapterView, View view, int i, long l, IDrawerItem item) {
|
public boolean onItemClick(AdapterView<?> adapterView, View view, int i, long
|
||||||
|
l, IDrawerItem item) {
|
||||||
if (item.getTag() instanceof Label) {
|
if (item.getTag() instanceof Label) {
|
||||||
selectedLabel = (Label) item.getTag();
|
selectedLabel = (Label) item.getTag();
|
||||||
showSelectedLabel();
|
showSelectedLabel();
|
||||||
@ -331,15 +330,18 @@ public class MainActivity extends AppCompatActivity
|
|||||||
Nameable<?> ni = (Nameable<?>) item;
|
Nameable<?> ni = (Nameable<?>) item;
|
||||||
switch (ni.getNameRes()) {
|
switch (ni.getNameRes()) {
|
||||||
case R.string.contacts_and_subscriptions:
|
case R.string.contacts_and_subscriptions:
|
||||||
if (!(getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof SubscriptionListFragment)) {
|
if (!(getSupportFragmentManager().findFragmentById(R.id
|
||||||
|
.item_list) instanceof SubscriptionListFragment)) {
|
||||||
changeList(new SubscriptionListFragment());
|
changeList(new SubscriptionListFragment());
|
||||||
} else {
|
} else {
|
||||||
((SubscriptionListFragment) getSupportFragmentManager()
|
((SubscriptionListFragment) getSupportFragmentManager()
|
||||||
.findFragmentById(R.id.item_list)).updateList();
|
.findFragmentById(R.id.item_list)).updateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case R.string.settings:
|
case R.string.settings:
|
||||||
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
|
startActivity(new Intent(MainActivity.this, SettingsActivity
|
||||||
|
.class));
|
||||||
break;
|
break;
|
||||||
case R.string.archive:
|
case R.string.archive:
|
||||||
selectedLabel = null;
|
selectedLabel = null;
|
||||||
@ -357,7 +359,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showSelectedLabel() {
|
private void showSelectedLabel() {
|
||||||
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof MessageListFragment) {
|
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
|
||||||
|
MessageListFragment) {
|
||||||
((MessageListFragment) getSupportFragmentManager()
|
((MessageListFragment) getSupportFragmentManager()
|
||||||
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
|
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
|
||||||
} else {
|
} else {
|
||||||
@ -385,7 +388,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
else if (item instanceof BitmessageAddress)
|
else if (item instanceof BitmessageAddress)
|
||||||
fragment = new SubscriptionDetailFragment();
|
fragment = new SubscriptionDetailFragment();
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but was "
|
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
|
||||||
|
"was "
|
||||||
+ item.getClass().getSimpleName());
|
+ item.getClass().getSimpleName());
|
||||||
fragment.setArguments(arguments);
|
fragment.setArguments(arguments);
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
@ -400,7 +404,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
else if (item instanceof BitmessageAddress)
|
else if (item instanceof BitmessageAddress)
|
||||||
detailIntent = new Intent(this, SubscriptionDetailActivity.class);
|
detailIntent = new Intent(this, SubscriptionDetailActivity.class);
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but was "
|
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
|
||||||
|
"was "
|
||||||
+ item.getClass().getSimpleName());
|
+ item.getClass().getSimpleName());
|
||||||
|
|
||||||
detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item);
|
detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item);
|
||||||
@ -421,7 +426,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE);
|
bindService(new Intent(this, BitmessageService.class), connection, Context
|
||||||
|
.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -463,8 +469,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
.withEmail(identity.getAddress())
|
.withEmail(identity.getAddress())
|
||||||
.withTag(identity);
|
.withTag(identity);
|
||||||
if (accountHeader.getProfiles() != null) {
|
if (accountHeader.getProfiles() != null) {
|
||||||
//we know that there are 2 setting elements. set the new profile above them ;)
|
//we know that there are 2 setting elements. set the new profile
|
||||||
accountHeader.addProfile(newProfile, accountHeader.getProfiles().size() - 2);
|
// above them ;)
|
||||||
|
accountHeader.addProfile(newProfile, accountHeader.getProfiles().size
|
||||||
|
() - 2);
|
||||||
} else {
|
} else {
|
||||||
accountHeader.addProfiles(newProfile);
|
accountHeader.addProfiles(newProfile);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ 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.DATA_FIELD_ADDRESS;
|
||||||
import static ch.dissem.apps.abit.service.BitmessageService.MSG_ADD_CONTACT;
|
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;
|
||||||
import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE_AND_ADD_CONTACT;
|
|
||||||
|
|
||||||
public class OpenBitmessageLinkActivity extends AppCompatActivity {
|
public class OpenBitmessageLinkActivity extends AppCompatActivity {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class);
|
||||||
@ -106,7 +105,7 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
final int what;
|
final int what;
|
||||||
if (subscribe.isChecked())
|
if (subscribe.isChecked())
|
||||||
what = MSG_SUBSCRIBE_AND_ADD_CONTACT;
|
what = MSG_SUBSCRIBE;
|
||||||
else
|
else
|
||||||
what = MSG_ADD_CONTACT;
|
what = MSG_ADD_CONTACT;
|
||||||
|
|
||||||
@ -155,7 +154,8 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
bindService(new Intent(this, BitmessageService.class), connection, Context.BIND_AUTO_CREATE);
|
bindService(new Intent(this, BitmessageService.class), connection, Context
|
||||||
|
.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,7 @@ import android.support.v7.app.AppCompatActivity;
|
|||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 14.07.15.
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
public class SettingsActivity extends AppCompatActivity {
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
package ch.dissem.apps.abit;
|
package ch.dissem.apps.abit;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
||||||
|
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 14.07.15.
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
public class SettingsFragment extends PreferenceFragment {
|
public class SettingsFragment
|
||||||
|
extends PreferenceFragment
|
||||||
|
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -15,4 +24,32 @@ public class SettingsFragment extends PreferenceFragment {
|
|||||||
// Load the preferences from an XML resource
|
// Load the preferences from an XML resource
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context ctx) {
|
||||||
|
super.onAttach(ctx);
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
|
.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
switch (key) {
|
||||||
|
case PREFERENCE_TRUSTED_NODE:
|
||||||
|
String node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null);
|
||||||
|
if (node != null) {
|
||||||
|
SyncAdapter.startSync(getActivity());
|
||||||
|
} else {
|
||||||
|
SyncAdapter.stopSync(getActivity());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREFERENCE_SERVER_POW:
|
||||||
|
if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
|
||||||
|
SyncAdapter.startPowSync(getActivity());
|
||||||
|
} else {
|
||||||
|
SyncAdapter.stopPowSync(getActivity());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package ch.dissem.apps.abit;
|
package ch.dissem.apps.abit;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -25,6 +26,7 @@ import android.widget.ArrayAdapter;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.listener.ActionBarListener;
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
@ -100,12 +102,18 @@ public class SubscriptionListFragment extends AbstractItemListFragment<Bitmessag
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context ctx) {
|
||||||
|
super.onAttach(ctx);
|
||||||
|
if (ctx instanceof ActionBarListener){
|
||||||
|
((ActionBarListener) ctx).updateTitle(getString(R.string.contacts_and_subscriptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View rootView = inflater.inflate(R.layout.fragment_contact_list, container, false);
|
return inflater.inflate(R.layout.fragment_contact_list, container, false);
|
||||||
|
|
||||||
return rootView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,57 +28,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
|||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
public class AndroidSecurity extends SpongySecurity {
|
public class AndroidSecurity extends SpongySecurity {
|
||||||
private final SharedPreferences preferences;
|
public AndroidSecurity() {
|
||||||
|
|
||||||
public AndroidSecurity(Context ctx) {
|
|
||||||
PRNGFixes.apply();
|
PRNGFixes.apply();
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes, ProofOfWorkEngine.Callback callback) {
|
|
||||||
if (preferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
|
|
||||||
object.setNonce(new byte[8]);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
object.write(out);
|
|
||||||
sendAsBroadcast(getContext().getAddressRepo().getIdentities().get(0), out.toByteArray());
|
|
||||||
if (object.getPayload() instanceof PlaintextHolder) {
|
|
||||||
Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext();
|
|
||||||
plaintext.setInventoryVector(object.getInventoryVector());
|
|
||||||
plaintext.setStatus(SENT);
|
|
||||||
plaintext.removeLabel(Label.Type.OUTBOX);
|
|
||||||
plaintext.addLabels(getContext().getMessageRepository().getLabels(Label.Type.SENT));
|
|
||||||
getContext().getMessageRepository().save(plaintext);
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
super.doProofOfWork(object, nonceTrialsPerByte, extraBytes, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendAsBroadcast(BitmessageAddress identity, byte[] data) throws IOException {
|
|
||||||
Plaintext msg = new Plaintext.Builder(BROADCAST)
|
|
||||||
.from(identity)
|
|
||||||
.message(data)
|
|
||||||
.build();
|
|
||||||
Broadcast payload = Factory.getBroadcast(identity, msg);
|
|
||||||
long expires = UnixTime.now(+2 * DAY);
|
|
||||||
final ObjectMessage object = new ObjectMessage.Builder()
|
|
||||||
.stream(identity.getStream())
|
|
||||||
.expiresTime(expires)
|
|
||||||
.payload(payload)
|
|
||||||
.build();
|
|
||||||
object.sign(identity.getPrivateKey());
|
|
||||||
payload.encrypt();
|
|
||||||
object.setNonce(new byte[8]);
|
|
||||||
|
|
||||||
getContext().getInventory().storeObject(object);
|
|
||||||
getContext().getNetworkHandler().offer(object.getInventoryVector());
|
|
||||||
// TODO: offer to the trusted node only?
|
|
||||||
// at least make sure it is offered to the trusted node!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package ch.dissem.apps.abit.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.util.Preferences;
|
||||||
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||||
|
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches between two {@link ProofOfWorkEngine}s depending on the configuration.
|
||||||
|
*
|
||||||
|
* @author Christian Basler
|
||||||
|
*/
|
||||||
|
public class SwitchingProofOfWorkEngine implements ProofOfWorkEngine, InternalContext.ContextHolder {
|
||||||
|
private final Context ctx;
|
||||||
|
private final String preference;
|
||||||
|
private final ProofOfWorkEngine option;
|
||||||
|
private final ProofOfWorkEngine fallback;
|
||||||
|
|
||||||
|
public SwitchingProofOfWorkEngine(Context ctx, String preference,
|
||||||
|
ProofOfWorkEngine option, ProofOfWorkEngine fallback) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.preference = preference;
|
||||||
|
this.option = option;
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
|
if (preferences.getBoolean(preference, false)) {
|
||||||
|
option.calculateNonce(initialHash, target, callback);
|
||||||
|
} else {
|
||||||
|
fallback.calculateNonce(initialHash, target, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(InternalContext context) {
|
||||||
|
for (ProofOfWorkEngine e : Arrays.asList(option, fallback)) {
|
||||||
|
if (e instanceof InternalContext.ContextHolder) {
|
||||||
|
((InternalContext.ContextHolder) e).setContext(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package ch.dissem.apps.abit.pow;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
|
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
||||||
|
import ch.dissem.apps.abit.util.Preferences;
|
||||||
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.extensions.CryptoCustomMessage;
|
||||||
|
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE;
|
||||||
|
import static ch.dissem.bitmessage.utils.Singleton.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Basler
|
||||||
|
*/
|
||||||
|
public class ServerPowEngine implements ProofOfWorkEngine, InternalContext
|
||||||
|
.ContextHolder {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ServerPowEngine.class);
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
private InternalContext context;
|
||||||
|
|
||||||
|
private final ExecutorService pool;
|
||||||
|
|
||||||
|
public ServerPowEngine(Context ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
pool = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||||
|
@Override
|
||||||
|
public Thread newThread(@NonNull Runnable r) {
|
||||||
|
Thread thread = Executors.defaultThreadFactory().newThread(r);
|
||||||
|
thread.setPriority(Thread.MIN_PRIORITY);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void calculateNonce(final byte[] initialHash, final byte[] target, Callback callback) {
|
||||||
|
pool.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
BitmessageAddress identity = Singleton.getIdentity(ctx);
|
||||||
|
if (identity == null) throw new RuntimeException("No Identity for calculating POW");
|
||||||
|
|
||||||
|
ProofOfWorkRequest request = new ProofOfWorkRequest(identity, initialHash,
|
||||||
|
CALCULATE, target);
|
||||||
|
SyncAdapter.startPowSync(ctx);
|
||||||
|
try {
|
||||||
|
CryptoCustomMessage<ProofOfWorkRequest> cryptoMsg = new CryptoCustomMessage<>
|
||||||
|
(request);
|
||||||
|
cryptoMsg.signAndEncrypt(
|
||||||
|
identity,
|
||||||
|
security().createPublicKey(identity.getPublicDecryptionKey())
|
||||||
|
);
|
||||||
|
context.getNetworkHandler().send(
|
||||||
|
Preferences.getTrustedNode(ctx), Preferences.getTrustedNodePort(ctx),
|
||||||
|
cryptoMsg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(InternalContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ package ch.dissem.apps.abit.repository;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
||||||
@ -27,6 +28,7 @@ import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
|||||||
import ch.dissem.bitmessage.factory.Factory;
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||||
import ch.dissem.bitmessage.utils.Encode;
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -118,27 +120,30 @@ public class AndroidAddressRepository implements AddressRepository {
|
|||||||
COLUMN_SUBSCRIBED
|
COLUMN_SUBSCRIBED
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor c = db.query(
|
Cursor c = db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
where,
|
where,
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
BitmessageAddress address;
|
BitmessageAddress address;
|
||||||
|
|
||||||
byte[] privateKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PRIVATE_KEY));
|
byte[] privateKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PRIVATE_KEY));
|
||||||
if (privateKeyBytes != null) {
|
if (privateKeyBytes != null) {
|
||||||
PrivateKey privateKey = PrivateKey.read(new ByteArrayInputStream(privateKeyBytes));
|
PrivateKey privateKey = PrivateKey.read(new ByteArrayInputStream
|
||||||
|
(privateKeyBytes));
|
||||||
address = new BitmessageAddress(privateKey);
|
address = new BitmessageAddress(privateKey);
|
||||||
} else {
|
} else {
|
||||||
address = new BitmessageAddress(c.getString(c.getColumnIndex(COLUMN_ADDRESS)));
|
address = new BitmessageAddress(c.getString(c.getColumnIndex(COLUMN_ADDRESS)));
|
||||||
byte[] publicKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PUBLIC_KEY));
|
byte[] publicKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PUBLIC_KEY));
|
||||||
if (publicKeyBytes != null) {
|
if (publicKeyBytes != null) {
|
||||||
Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(),
|
Pubkey pubkey = Factory.readPubkey(address.getVersion(), address
|
||||||
new ByteArrayInputStream(publicKeyBytes), publicKeyBytes.length, false);
|
.getStream(),
|
||||||
|
new ByteArrayInputStream(publicKeyBytes), publicKeyBytes.length,
|
||||||
|
false);
|
||||||
if (address.getVersion() == 4 && pubkey instanceof V3Pubkey) {
|
if (address.getVersion() == 4 && pubkey instanceof V3Pubkey) {
|
||||||
pubkey = new V4Pubkey((V3Pubkey) pubkey);
|
pubkey = new V4Pubkey((V3Pubkey) pubkey);
|
||||||
}
|
}
|
||||||
@ -153,8 +158,9 @@ public class AndroidAddressRepository implements AddressRepository {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,9 +179,14 @@ public class AndroidAddressRepository implements AddressRepository {
|
|||||||
|
|
||||||
private boolean exists(BitmessageAddress address) {
|
private boolean exists(BitmessageAddress address) {
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'", null);
|
Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM Address WHERE address='" + address
|
||||||
|
.getAddress() + "'", null);
|
||||||
|
try {
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
return cursor.getInt(0) > 0;
|
return cursor.getInt(0) > 0;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update(BitmessageAddress address) throws IOException {
|
private void update(BitmessageAddress address) throws IOException {
|
||||||
@ -194,7 +205,8 @@ public class AndroidAddressRepository implements AddressRepository {
|
|||||||
values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey()));
|
values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey()));
|
||||||
values.put(COLUMN_SUBSCRIBED, address.isSubscribed());
|
values.put(COLUMN_SUBSCRIBED, address.isSubscribed());
|
||||||
|
|
||||||
int update = db.update(TABLE_NAME, values, "address = '" + address.getAddress() + "'", null);
|
int update = db.update(TABLE_NAME, values, "address = '" + address.getAddress() +
|
||||||
|
"'", null);
|
||||||
if (update < 0) {
|
if (update < 0) {
|
||||||
LOG.error("Could not update address " + address);
|
LOG.error("Could not update address " + address);
|
||||||
}
|
}
|
||||||
|
@ -74,16 +74,21 @@ public class AndroidInventory implements Inventory {
|
|||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor c = db.query(
|
Cursor c = db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
(includeExpired ? "" : "expires > " + now() + " AND ") + "stream IN (" + join(streams) + ")",
|
(includeExpired ? "" : "expires > " + now() + " AND ") + "stream IN (" + join
|
||||||
|
(streams) + ")",
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
c.moveToFirst();
|
|
||||||
List<InventoryVector> result = new LinkedList<>();
|
List<InventoryVector> result = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
|
||||||
result.add(new InventoryVector(blob));
|
result.add(new InventoryVector(blob));
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +113,7 @@ public class AndroidInventory implements Inventory {
|
|||||||
"hash = X'" + vector + "'",
|
"hash = X'" + vector + "'",
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
if (c.isAfterLast()) {
|
if (c.isAfterLast()) {
|
||||||
LOG.info("Object requested that we don't have. IV: " + vector);
|
LOG.info("Object requested that we don't have. IV: " + vector);
|
||||||
@ -117,6 +123,9 @@ public class AndroidInventory implements Inventory {
|
|||||||
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
||||||
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
return Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob.length);
|
return Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob.length);
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -144,14 +153,19 @@ public class AndroidInventory implements Inventory {
|
|||||||
where.toString(),
|
where.toString(),
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
c.moveToFirst();
|
|
||||||
List<ObjectMessage> result = new LinkedList<>();
|
List<ObjectMessage> result = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
||||||
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
result.add(Factory.getObjectMessage(objectVersion, new ByteArrayInputStream(blob), blob.length));
|
result.add(Factory.getObjectMessage(objectVersion, new ByteArrayInputStream(blob),
|
||||||
|
blob.length));
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +201,11 @@ public class AndroidInventory implements Inventory {
|
|||||||
"hash = X'" + object.getInventoryVector() + "'",
|
"hash = X'" + object.getInventoryVector() + "'",
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
return c.getCount() > 0;
|
return c.getCount() > 0;
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -30,6 +30,7 @@ import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
|||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
import ch.dissem.bitmessage.utils.Encode;
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
|
import ch.dissem.bitmessage.utils.Strings;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -58,6 +59,7 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
private static final String COLUMN_SENT = "sent";
|
private static final String COLUMN_SENT = "sent";
|
||||||
private static final String COLUMN_RECEIVED = "received";
|
private static final String COLUMN_RECEIVED = "received";
|
||||||
private static final String COLUMN_STATUS = "status";
|
private static final String COLUMN_STATUS = "status";
|
||||||
|
private static final String COLUMN_INITIAL_HASH = "initial_hash";
|
||||||
|
|
||||||
private static final String JOIN_TABLE_NAME = "Message_Label";
|
private static final String JOIN_TABLE_NAME = "Message_Label";
|
||||||
private static final String JT_COLUMN_MESSAGE = "message_id";
|
private static final String JT_COLUMN_MESSAGE = "message_id";
|
||||||
@ -112,11 +114,15 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
null, null, null,
|
null, null, null,
|
||||||
LBL_COLUMN_ORDER
|
LBL_COLUMN_ORDER
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
result.add(getLabel(c));
|
result.add(getLabel(c));
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +130,9 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
String typeName = c.getString(c.getColumnIndex(LBL_COLUMN_TYPE));
|
String typeName = c.getString(c.getColumnIndex(LBL_COLUMN_TYPE));
|
||||||
Label.Type type = typeName == null ? null : Label.Type.valueOf(typeName);
|
Label.Type type = typeName == null ? null : Label.Type.valueOf(typeName);
|
||||||
String text;
|
String text;
|
||||||
|
if (type == null) {
|
||||||
|
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
||||||
|
} else {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INBOX:
|
case INBOX:
|
||||||
text = ctx.getString(R.string.inbox);
|
text = ctx.getString(R.string.inbox);
|
||||||
@ -146,6 +155,7 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
default:
|
default:
|
||||||
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Label label = new Label(
|
Label label = new Label(
|
||||||
text,
|
text,
|
||||||
type,
|
type,
|
||||||
@ -158,7 +168,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
public int countUnread(Label label) {
|
public int countUnread(Label label) {
|
||||||
String where;
|
String where;
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND ";
|
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId()
|
||||||
|
+ ") AND ";
|
||||||
} else {
|
} else {
|
||||||
where = "";
|
where = "";
|
||||||
}
|
}
|
||||||
@ -169,13 +180,32 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
"SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))",
|
"SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))",
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
return c.getColumnCount();
|
return c.getColumnCount();
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Plaintext getMessage(byte[] initialHash) {
|
||||||
|
List<Plaintext> results = find("initial_hash=X'" + Strings.hex(initialHash) + "'");
|
||||||
|
switch (results.size()) {
|
||||||
|
case 0:
|
||||||
|
return null;
|
||||||
|
case 1:
|
||||||
|
return results.get(0);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("This shouldn't happen, found " + results.size() +
|
||||||
|
" messages, one or none was expected");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Plaintext> findMessages(Label label) {
|
public List<Plaintext> findMessages(Label label) {
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")");
|
return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label
|
||||||
|
.getId() + ")");
|
||||||
} else {
|
} else {
|
||||||
return find("id NOT IN (SELECT message_id FROM Message_Label)");
|
return find("id NOT IN (SELECT message_id FROM Message_Label)");
|
||||||
}
|
}
|
||||||
@ -183,7 +213,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) {
|
public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) {
|
||||||
return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() + "'");
|
return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() +
|
||||||
|
"'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -213,7 +244,6 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
COLUMN_STATUS
|
COLUMN_STATUS
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor c = db.query(
|
Cursor c = db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
@ -221,26 +251,34 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
null, null, null,
|
null, null, null,
|
||||||
COLUMN_RECEIVED + " DESC"
|
COLUMN_RECEIVED + " DESC"
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
byte[] iv = c.getBlob(c.getColumnIndex(COLUMN_IV));
|
byte[] iv = c.getBlob(c.getColumnIndex(COLUMN_IV));
|
||||||
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex(COLUMN_TYPE)));
|
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex
|
||||||
Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new ByteArrayInputStream(data));
|
(COLUMN_TYPE)));
|
||||||
|
Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new
|
||||||
|
ByteArrayInputStream(data));
|
||||||
long id = c.getLong(c.getColumnIndex(COLUMN_ID));
|
long id = c.getLong(c.getColumnIndex(COLUMN_ID));
|
||||||
builder.id(id);
|
builder.id(id);
|
||||||
builder.IV(new InventoryVector(iv));
|
builder.IV(new InventoryVector(iv));
|
||||||
builder.from(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_SENDER))));
|
builder.from(bmc.getAddressRepository().getAddress(c.getString(c.getColumnIndex
|
||||||
builder.to(bmc.getAddressRepo().getAddress(c.getString(c.getColumnIndex(COLUMN_RECIPIENT))));
|
(COLUMN_SENDER))));
|
||||||
|
builder.to(bmc.getAddressRepository().getAddress(c.getString(c.getColumnIndex
|
||||||
|
(COLUMN_RECIPIENT))));
|
||||||
builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT)));
|
builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT)));
|
||||||
builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED)));
|
builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED)));
|
||||||
builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS))));
|
builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex
|
||||||
|
(COLUMN_STATUS))));
|
||||||
builder.labels(findLabels(id));
|
builder.labels(findLabels(id));
|
||||||
result.add(builder.build());
|
result.add(builder.build());
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -257,12 +295,13 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
|
|
||||||
// save from address if necessary
|
// save from address if necessary
|
||||||
if (message.getId() == null) {
|
if (message.getId() == null) {
|
||||||
BitmessageAddress savedAddress = bmc.getAddressRepo().getAddress(message.getFrom().getAddress());
|
BitmessageAddress savedAddress = bmc.getAddressRepository().getAddress(message
|
||||||
|
.getFrom().getAddress());
|
||||||
if (savedAddress == null || savedAddress.getPrivateKey() == null) {
|
if (savedAddress == null || savedAddress.getPrivateKey() == null) {
|
||||||
if (savedAddress != null && savedAddress.getAlias() != null) {
|
if (savedAddress != null && savedAddress.getAlias() != null) {
|
||||||
message.getFrom().setAlias(savedAddress.getAlias());
|
message.getFrom().setAlias(savedAddress.getAlias());
|
||||||
}
|
}
|
||||||
bmc.getAddressRepo().save(message.getFrom());
|
bmc.getAddressRepository().save(message.getFrom());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +334,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
|
|
||||||
private void insert(SQLiteDatabase db, Plaintext message) throws IOException {
|
private void insert(SQLiteDatabase db, Plaintext message) throws IOException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(COLUMN_IV, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
|
values.put(COLUMN_IV, message.getInventoryVector() == null ? null : message
|
||||||
|
.getInventoryVector().getHash());
|
||||||
values.put(COLUMN_TYPE, message.getType().name());
|
values.put(COLUMN_TYPE, message.getType().name());
|
||||||
values.put(COLUMN_SENDER, message.getFrom().getAddress());
|
values.put(COLUMN_SENDER, message.getFrom().getAddress());
|
||||||
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
|
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
|
||||||
@ -303,13 +343,15 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
values.put(COLUMN_SENT, message.getSent());
|
values.put(COLUMN_SENT, message.getSent());
|
||||||
values.put(COLUMN_RECEIVED, message.getReceived());
|
values.put(COLUMN_RECEIVED, message.getReceived());
|
||||||
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
|
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
|
||||||
|
values.put(COLUMN_INITIAL_HASH, message.getInitialHash());
|
||||||
long id = db.insertOrThrow(TABLE_NAME, null, values);
|
long id = db.insertOrThrow(TABLE_NAME, null, values);
|
||||||
message.setId(id);
|
message.setId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update(SQLiteDatabase db, Plaintext message) throws IOException {
|
private void update(SQLiteDatabase db, Plaintext message) throws IOException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(COLUMN_IV, message.getInventoryVector() == null ? null : message.getInventoryVector().getHash());
|
values.put(COLUMN_IV, message.getInventoryVector() == null ? null : message
|
||||||
|
.getInventoryVector().getHash());
|
||||||
values.put(COLUMN_TYPE, message.getType().name());
|
values.put(COLUMN_TYPE, message.getType().name());
|
||||||
values.put(COLUMN_SENDER, message.getFrom().getAddress());
|
values.put(COLUMN_SENDER, message.getFrom().getAddress());
|
||||||
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
|
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
|
||||||
@ -317,6 +359,7 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
|
|||||||
values.put(COLUMN_SENT, message.getSent());
|
values.put(COLUMN_SENT, message.getSent());
|
||||||
values.put(COLUMN_RECEIVED, message.getReceived());
|
values.put(COLUMN_RECEIVED, message.getReceived());
|
||||||
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
|
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
|
||||||
|
values.put(COLUMN_INITIAL_HASH, message.getInitialHash());
|
||||||
db.update(TABLE_NAME, values, "id = " + message.getId(), null);
|
db.update(TABLE_NAME, values, "id = " + message.getId(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,133 @@
|
|||||||
|
package ch.dissem.apps.abit.repository;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteConstraintException;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
|
import ch.dissem.bitmessage.utils.Strings;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.utils.Singleton.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Basler
|
||||||
|
*/
|
||||||
|
public class AndroidProofOfWorkRepository implements ProofOfWorkRepository {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AndroidProofOfWorkRepository.class);
|
||||||
|
|
||||||
|
private static final String TABLE_NAME = "POW";
|
||||||
|
private static final String COLUMN_INITIAL_HASH = "initial_hash";
|
||||||
|
private static final String COLUMN_DATA = "data";
|
||||||
|
private static final String COLUMN_VERSION = "version";
|
||||||
|
private static final String COLUMN_NONCE_TRIALS_PER_BYTE = "nonce_trials_per_byte";
|
||||||
|
private static final String COLUMN_EXTRA_BYTES = "extra_bytes";
|
||||||
|
|
||||||
|
private final SqlHelper sql;
|
||||||
|
|
||||||
|
public AndroidProofOfWorkRepository(SqlHelper sql) {
|
||||||
|
this.sql = sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getItem(byte[] initialHash) {
|
||||||
|
// Define a projection that specifies which columns from the database
|
||||||
|
// you will actually use after this query.
|
||||||
|
String[] projection = {
|
||||||
|
COLUMN_DATA,
|
||||||
|
COLUMN_VERSION,
|
||||||
|
COLUMN_NONCE_TRIALS_PER_BYTE,
|
||||||
|
COLUMN_EXTRA_BYTES
|
||||||
|
};
|
||||||
|
|
||||||
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
|
Cursor c = db.query(
|
||||||
|
TABLE_NAME, projection,
|
||||||
|
"initial_hash = X'" + Strings.hex(initialHash) + "'",
|
||||||
|
null, null, null, null
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
c.moveToFirst();
|
||||||
|
if (!c.isAfterLast()) {
|
||||||
|
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
||||||
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
|
return new Item(
|
||||||
|
Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob
|
||||||
|
.length),
|
||||||
|
c.getLong(c.getColumnIndex(COLUMN_NONCE_TRIALS_PER_BYTE)),
|
||||||
|
c.getLong(c.getColumnIndex(COLUMN_EXTRA_BYTES))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Object requested that we don't have. Initial hash: " +
|
||||||
|
Strings.hex(initialHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<byte[]> getItems() {
|
||||||
|
// Define a projection that specifies which columns from the database
|
||||||
|
// you will actually use after this query.
|
||||||
|
String[] projection = {
|
||||||
|
COLUMN_INITIAL_HASH
|
||||||
|
};
|
||||||
|
|
||||||
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
|
Cursor c = db.query(
|
||||||
|
TABLE_NAME, projection,
|
||||||
|
null, null, null, null, null
|
||||||
|
);
|
||||||
|
List<byte[]> result = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
c.moveToFirst();
|
||||||
|
while (!c.isAfterLast()) {
|
||||||
|
byte[] initialHash = c.getBlob(c.getColumnIndex(COLUMN_INITIAL_HASH));
|
||||||
|
result.add(initialHash);
|
||||||
|
c.moveToNext();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = sql.getWritableDatabase();
|
||||||
|
// Create a new map of values, where column names are the keys
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_INITIAL_HASH, security().getInitialHash(object));
|
||||||
|
values.put(COLUMN_DATA, Encode.bytes(object));
|
||||||
|
values.put(COLUMN_VERSION, object.getVersion());
|
||||||
|
values.put(COLUMN_NONCE_TRIALS_PER_BYTE, nonceTrialsPerByte);
|
||||||
|
values.put(COLUMN_EXTRA_BYTES, extraBytes);
|
||||||
|
|
||||||
|
db.insertOrThrow(TABLE_NAME, null, values);
|
||||||
|
} catch (SQLiteConstraintException e) {
|
||||||
|
LOG.trace(e.getMessage(), e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObject(byte[] initialHash) {
|
||||||
|
SQLiteDatabase db = sql.getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME,
|
||||||
|
"initial_hash = X'" + Strings.hex(initialHash) + "'",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ import ch.dissem.apps.abit.util.Assets;
|
|||||||
*/
|
*/
|
||||||
public class SqlHelper extends SQLiteOpenHelper {
|
public class SqlHelper extends SQLiteOpenHelper {
|
||||||
// If you change the database schema, you must increment the database version.
|
// If you change the database schema, you must increment the database version.
|
||||||
public static final int DATABASE_VERSION = 1;
|
public static final int DATABASE_VERSION = 2;
|
||||||
public static final String DATABASE_NAME = "jabit.db";
|
public static final String DATABASE_NAME = "jabit.db";
|
||||||
|
|
||||||
protected final Context ctx;
|
protected final Context ctx;
|
||||||
@ -38,7 +38,7 @@ public class SqlHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
onUpgrade(db, 0, 1);
|
onUpgrade(db, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -48,6 +48,9 @@ public class SqlHelper extends SQLiteOpenHelper {
|
|||||||
executeMigration(db, "V1.0__Create_table_inventory");
|
executeMigration(db, "V1.0__Create_table_inventory");
|
||||||
executeMigration(db, "V1.1__Create_table_address");
|
executeMigration(db, "V1.1__Create_table_address");
|
||||||
executeMigration(db, "V1.2__Create_table_message");
|
executeMigration(db, "V1.2__Create_table_message");
|
||||||
|
case 1:
|
||||||
|
// executeMigration(db, "V2.0__Update_table_message");
|
||||||
|
executeMigration(db, "V2.1__Create_table_POW");
|
||||||
default:
|
default:
|
||||||
// Nothing to do. Let's assume we won't upgrade from a version that's newer than DATABASE_VERSION.
|
// Nothing to do. Let's assume we won't upgrade from a version that's newer than DATABASE_VERSION.
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ public class BitmessageService extends Service {
|
|||||||
public static final int MSG_CREATE_IDENTITY = 10;
|
public static final int MSG_CREATE_IDENTITY = 10;
|
||||||
public static final int MSG_SUBSCRIBE = 20;
|
public static final int MSG_SUBSCRIBE = 20;
|
||||||
public static final int MSG_ADD_CONTACT = 21;
|
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_MESSAGE = 30;
|
||||||
public static final int MSG_SEND_BROADCAST = 31;
|
public static final int MSG_SEND_BROADCAST = 31;
|
||||||
public static final int MSG_START_NODE = 100;
|
public static final int MSG_START_NODE = 100;
|
||||||
@ -122,6 +121,13 @@ public class BitmessageService extends Service {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case MSG_ADD_CONTACT: {
|
||||||
|
Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
||||||
|
if (data instanceof BitmessageAddress) {
|
||||||
|
bmc.addContact((BitmessageAddress) data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case MSG_SEND_MESSAGE: {
|
case MSG_SEND_MESSAGE: {
|
||||||
Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
|
Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
|
||||||
Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
|
||||||
|
@ -73,9 +73,9 @@ public class ProofOfWorkService extends Service {
|
|||||||
service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
|
service.startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
|
||||||
engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() {
|
engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onNonceCalculated(byte[] nonce) {
|
public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
|
||||||
try {
|
try {
|
||||||
callback.onNonceCalculated(nonce);
|
callback.onNonceCalculated(initialHash, nonce);
|
||||||
} finally {
|
} finally {
|
||||||
service.stopForeground(true);
|
service.stopForeground(true);
|
||||||
service.stopSelf();
|
service.stopSelf();
|
||||||
|
@ -53,8 +53,8 @@ public class ServicePowEngine implements ProofOfWorkEngine, ProofOfWorkEngine.Ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNonceCalculated(byte[] bytes) {
|
public void onNonceCalculated(byte[] initialHash, byte[] bytes) {
|
||||||
callback.onNonceCalculated(bytes);
|
callback.onNonceCalculated(initialHash, bytes);
|
||||||
ctx.unbindService(connection);
|
ctx.unbindService(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,18 +2,27 @@ package ch.dissem.apps.abit.service;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ch.dissem.apps.abit.adapter.AndroidSecurity;
|
import ch.dissem.apps.abit.adapter.AndroidSecurity;
|
||||||
|
import ch.dissem.apps.abit.adapter.SwitchingProofOfWorkEngine;
|
||||||
import ch.dissem.apps.abit.listener.MessageListener;
|
import ch.dissem.apps.abit.listener.MessageListener;
|
||||||
|
import ch.dissem.apps.abit.pow.ServerPowEngine;
|
||||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
|
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
|
||||||
import ch.dissem.apps.abit.repository.AndroidInventory;
|
import ch.dissem.apps.abit.repository.AndroidInventory;
|
||||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository;
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository;
|
||||||
|
import ch.dissem.apps.abit.repository.AndroidProofOfWorkRepository;
|
||||||
import ch.dissem.apps.abit.repository.SqlHelper;
|
import ch.dissem.apps.abit.repository.SqlHelper;
|
||||||
|
import ch.dissem.apps.abit.util.Constants;
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
||||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||||
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
import ch.dissem.bitmessage.security.sc.SpongySecurity;
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides singleton objects across the application.
|
* Provides singleton objects across the application.
|
||||||
@ -22,6 +31,8 @@ public class Singleton {
|
|||||||
public static final Object lock = new Object();
|
public static final Object lock = new Object();
|
||||||
private static BitmessageContext bitmessageContext;
|
private static BitmessageContext bitmessageContext;
|
||||||
private static MessageListener messageListener;
|
private static MessageListener messageListener;
|
||||||
|
private static BitmessageAddress identity;
|
||||||
|
private static AndroidProofOfWorkRepository powRepo;
|
||||||
|
|
||||||
public static BitmessageContext getBitmessageContext(Context context) {
|
public static BitmessageContext getBitmessageContext(Context context) {
|
||||||
if (bitmessageContext == null) {
|
if (bitmessageContext == null) {
|
||||||
@ -29,15 +40,23 @@ public class Singleton {
|
|||||||
if (bitmessageContext == null) {
|
if (bitmessageContext == null) {
|
||||||
final Context ctx = context.getApplicationContext();
|
final Context ctx = context.getApplicationContext();
|
||||||
SqlHelper sqlHelper = new SqlHelper(ctx);
|
SqlHelper sqlHelper = new SqlHelper(ctx);
|
||||||
|
powRepo = new AndroidProofOfWorkRepository(sqlHelper);
|
||||||
bitmessageContext = new BitmessageContext.Builder()
|
bitmessageContext = new BitmessageContext.Builder()
|
||||||
.proofOfWorkEngine(new ServicePowEngine(ctx))
|
.proofOfWorkEngine(new SwitchingProofOfWorkEngine(
|
||||||
.security(new AndroidSecurity(ctx))
|
ctx, Constants.PREFERENCE_SERVER_POW,
|
||||||
|
new ServerPowEngine(ctx),
|
||||||
|
new ServicePowEngine(ctx)
|
||||||
|
))
|
||||||
|
.security(new AndroidSecurity())
|
||||||
.nodeRegistry(new MemoryNodeRegistry())
|
.nodeRegistry(new MemoryNodeRegistry())
|
||||||
.inventory(new AndroidInventory(sqlHelper))
|
.inventory(new AndroidInventory(sqlHelper))
|
||||||
.addressRepo(new AndroidAddressRepository(sqlHelper))
|
.addressRepo(new AndroidAddressRepository(sqlHelper))
|
||||||
.messageRepo(new AndroidMessageRepository(sqlHelper, ctx))
|
.messageRepo(new AndroidMessageRepository(sqlHelper, ctx))
|
||||||
|
.powRepo(powRepo)
|
||||||
.networkHandler(new DefaultNetworkHandler())
|
.networkHandler(new DefaultNetworkHandler())
|
||||||
.listener(getMessageListener(ctx))
|
.listener(getMessageListener(ctx))
|
||||||
|
.doNotSendPubkeyOnIdentityCreation()
|
||||||
|
.pubkeyTTL(2 * DAY)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,4 +82,24 @@ public class Singleton {
|
|||||||
public static AddressRepository getAddressRepository(Context ctx) {
|
public static AddressRepository getAddressRepository(Context ctx) {
|
||||||
return getBitmessageContext(ctx).addresses();
|
return getBitmessageContext(ctx).addresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ProofOfWorkRepository getProofOfWorkRepository(Context ctx) {
|
||||||
|
if (powRepo == null) getBitmessageContext(ctx);
|
||||||
|
return powRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BitmessageAddress getIdentity(Context ctx) {
|
||||||
|
if (identity == null) {
|
||||||
|
synchronized (Singleton.class) {
|
||||||
|
if (identity == null) {
|
||||||
|
List<BitmessageAddress> identities = getBitmessageContext(ctx).addresses()
|
||||||
|
.getIdentities();
|
||||||
|
if (identities.size() > 0) {
|
||||||
|
identity = identities.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ import android.os.Bundle;
|
|||||||
* of its methods
|
* of its methods
|
||||||
*/
|
*/
|
||||||
public class Authenticator extends AbstractAccountAuthenticator {
|
public class Authenticator extends AbstractAccountAuthenticator {
|
||||||
public static final String ACCOUNT_NAME = "Bitmessage";
|
public static final Account ACCOUNT_SYNC = new Account("Bitmessage", "ch.dissem.bitmessage");
|
||||||
public static final String ACCOUNT_TYPE = "ch.dissem.bitmessage";
|
public static final Account ACCOUNT_POW = new Account("Proof of Work ", "ch.dissem.bitmessage");
|
||||||
|
|
||||||
// Simple constructor
|
// Simple constructor
|
||||||
public Authenticator(Context context) {
|
public Authenticator(Context context) {
|
||||||
|
@ -1,27 +1,34 @@
|
|||||||
package ch.dissem.apps.abit.synchronization;
|
package ch.dissem.apps.abit.synchronization;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
import android.content.AbstractThreadedSyncAdapter;
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.util.List;
|
||||||
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.apps.abit.service.Singleton;
|
||||||
|
import ch.dissem.apps.abit.util.Preferences;
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.CustomMessage;
|
||||||
|
import ch.dissem.bitmessage.extensions.CryptoCustomMessage;
|
||||||
|
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
|
|
||||||
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT;
|
import static ch.dissem.apps.abit.synchronization.Authenticator.ACCOUNT_POW;
|
||||||
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
import static ch.dissem.apps.abit.synchronization.Authenticator.ACCOUNT_SYNC;
|
||||||
|
import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY;
|
||||||
|
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE;
|
||||||
|
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.COMPLETE;
|
||||||
|
import static ch.dissem.bitmessage.utils.Singleton.security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
||||||
@ -30,6 +37,8 @@ import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
|||||||
public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(SyncAdapter.class);
|
private final static Logger LOG = LoggerFactory.getLogger(SyncAdapter.class);
|
||||||
|
|
||||||
|
private static final long SYNC_FREQUENCY = 15 * 60; // seconds
|
||||||
|
|
||||||
private final BitmessageContext bmc;
|
private final BitmessageContext bmc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +50,17 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
public void onPerformSync(Account account, Bundle extras, String authority,
|
||||||
|
ContentProviderClient provider, SyncResult syncResult) {
|
||||||
|
if (account.equals(Authenticator.ACCOUNT_SYNC))
|
||||||
|
syncData();
|
||||||
|
else if (account.equals(Authenticator.ACCOUNT_POW))
|
||||||
|
syncPOW();
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Unknown " + account);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncData() {
|
||||||
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||||
if (bmc.isRunning()) {
|
if (bmc.isRunning()) {
|
||||||
LOG.info("Synchronization skipped, Abit is acting as a full node");
|
LOG.info("Synchronization skipped, Abit is acting as a full node");
|
||||||
@ -49,40 +68,103 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
}
|
}
|
||||||
LOG.info("Synchronizing Bitmessage");
|
LOG.info("Synchronizing Bitmessage");
|
||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
||||||
|
|
||||||
String trustedNode = preferences.getString(PREFERENCE_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(PREFERENCE_SYNC_TIMEOUT, "120"));
|
|
||||||
try {
|
try {
|
||||||
LOG.info("Synchronization started");
|
LOG.info("Synchronization started");
|
||||||
bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
|
bmc.synchronize(
|
||||||
|
Preferences.getTrustedNode(getContext()),
|
||||||
|
Preferences.getTrustedNodePort(getContext()),
|
||||||
|
Preferences.getTimeoutInSeconds(getContext()),
|
||||||
|
true);
|
||||||
LOG.info("Synchronization finished");
|
LOG.info("Synchronization finished");
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
new ErrorNotification(getContext())
|
|
||||||
.setError(R.string.error_invalid_sync_host)
|
|
||||||
.show();
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void syncPOW() {
|
||||||
|
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||||
|
LOG.info("Looking for completed POW");
|
||||||
|
|
||||||
|
try {
|
||||||
|
BitmessageAddress identity = Singleton.getIdentity(getContext());
|
||||||
|
byte[] privateKey = identity.getPrivateKey().getPrivateEncryptionKey();
|
||||||
|
byte[] signingKey = security().createPublicKey(identity.getPublicDecryptionKey());
|
||||||
|
ProofOfWorkRequest.Reader reader = new ProofOfWorkRequest.Reader(identity);
|
||||||
|
ProofOfWorkRepository powRepo = Singleton.getProofOfWorkRepository(getContext());
|
||||||
|
List<byte[]> items = powRepo.getItems();
|
||||||
|
for (byte[] initialHash : items) {
|
||||||
|
ProofOfWorkRepository.Item item = powRepo.getItem(initialHash);
|
||||||
|
byte[] target = security().getProofOfWorkTarget(item.object, item
|
||||||
|
.nonceTrialsPerByte, item.extraBytes);
|
||||||
|
CryptoCustomMessage<ProofOfWorkRequest> cryptoMsg = new CryptoCustomMessage<>(
|
||||||
|
new ProofOfWorkRequest(identity, initialHash, CALCULATE, target));
|
||||||
|
cryptoMsg.signAndEncrypt(identity, signingKey);
|
||||||
|
CustomMessage response = bmc.send(
|
||||||
|
Preferences.getTrustedNode(getContext()),
|
||||||
|
Preferences.getTrustedNodePort(getContext()),
|
||||||
|
cryptoMsg
|
||||||
|
);
|
||||||
|
if (response.isError()) {
|
||||||
|
LOG.error("Server responded with error: " + new String(response.getData(),
|
||||||
|
"UTF-8"));
|
||||||
|
} else {
|
||||||
|
ProofOfWorkRequest decryptedResponse = CryptoCustomMessage.read(
|
||||||
|
response, reader).decrypt(privateKey);
|
||||||
|
if (decryptedResponse.getRequest() == COMPLETE) {
|
||||||
|
bmc.internals().getProofOfWorkService().onNonceCalculated(
|
||||||
|
initialHash, decryptedResponse.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (items.size() == 0) {
|
||||||
|
stopPowSync(getContext());
|
||||||
|
}
|
||||||
|
LOG.info("Synchronization finished");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startSync(Context ctx) {
|
||||||
|
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||||
|
Account account = addAccount(ctx, ACCOUNT_SYNC);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopSync(Context ctx) {
|
||||||
|
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||||
|
Account account = addAccount(ctx, ACCOUNT_SYNC);
|
||||||
|
|
||||||
|
ContentResolver.removePeriodicSync(account, AUTHORITY, new Bundle());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void startPowSync(Context ctx) {
|
||||||
|
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||||
|
Account account = addAccount(ctx, ACCOUNT_POW);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopPowSync(Context ctx) {
|
||||||
|
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||||
|
Account account = addAccount(ctx, ACCOUNT_POW);
|
||||||
|
|
||||||
|
ContentResolver.removePeriodicSync(account, AUTHORITY, new Bundle());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Account addAccount(Context ctx, Account account) {
|
||||||
|
if (AccountManager.get(ctx).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);
|
||||||
|
}
|
||||||
|
return account;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
90
app/src/main/java/ch/dissem/apps/abit/util/Preferences.java
Normal file
90
app/src/main/java/ch/dissem/apps/abit/util/Preferences.java
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package ch.dissem.apps.abit.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
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 static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT;
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by chrig on 01.12.2015.
|
||||||
|
*/
|
||||||
|
public class Preferences {
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(Preferences.class);
|
||||||
|
|
||||||
|
public static boolean useTrustedNode(Context ctx) {
|
||||||
|
String trustedNode = getPreference(ctx, PREFERENCE_TRUSTED_NODE);
|
||||||
|
return trustedNode == null || trustedNode.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning, this method might do a network call and therefore can't be called from
|
||||||
|
* the UI thread.
|
||||||
|
*/
|
||||||
|
public static InetAddress getTrustedNode(Context ctx) {
|
||||||
|
String trustedNode = getPreference(ctx, PREFERENCE_TRUSTED_NODE);
|
||||||
|
if (trustedNode == null) return null;
|
||||||
|
trustedNode = trustedNode.trim();
|
||||||
|
if (trustedNode.isEmpty()) return null;
|
||||||
|
|
||||||
|
if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$")) {
|
||||||
|
int index = trustedNode.lastIndexOf(':');
|
||||||
|
trustedNode = trustedNode.substring(0, index);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(trustedNode);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
new ErrorNotification(ctx)
|
||||||
|
.setError(R.string.error_invalid_sync_host)
|
||||||
|
.show();
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getTrustedNodePort(Context ctx) {
|
||||||
|
String trustedNode = getPreference(ctx, PREFERENCE_TRUSTED_NODE);
|
||||||
|
if (trustedNode == null) return 8444;
|
||||||
|
trustedNode = trustedNode.trim();
|
||||||
|
|
||||||
|
if (trustedNode.matches("^(?![0-9a-fA-F]*:[0-9a-fA-F]*:).*(:[0-9]+)$")) {
|
||||||
|
int index = trustedNode.lastIndexOf(':');
|
||||||
|
String portString = trustedNode.substring(index + 1);
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(portString);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
new ErrorNotification(ctx)
|
||||||
|
.setError(R.string.error_invalid_sync_port, portString)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 8444;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getTimeoutInSeconds(Context ctx) {
|
||||||
|
String preference = getPreference(ctx, PREFERENCE_SYNC_TIMEOUT);
|
||||||
|
return preference == null ? 120 : Long.parseLong(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isServerPOW(Context ctx) {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
|
return preferences.getBoolean(PREFERENCE_SERVER_POW, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPreference(Context ctx, String name) {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
|
|
||||||
|
return preferences.getString(name, null);
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@
|
|||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="server_pow"
|
android:key="server_pow"
|
||||||
android:dependency="@string/trusted_node"
|
android:dependency="trusted_node"
|
||||||
android:title="@string/server_pow"
|
android:title="@string/server_pow"
|
||||||
android:summary="@string/server_pow_summary"
|
android:summary="@string/server_pow_summary"
|
||||||
/>
|
/>
|
||||||
|
@ -9,7 +9,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:1.5.0-beta1'
|
classpath 'com.android.tools.build:gradle:1.5.0'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
Loading…
Reference in New Issue
Block a user