From bf52d2f3de4e2eb78026cefe99b647dbf399ddeb Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Thu, 29 Jun 2017 23:29:56 +0200 Subject: [PATCH] Asynchronously load contacts to improve responsiveness --- .../apps/abit/AddressDetailFragment.java | 3 - .../dissem/apps/abit/AddressListFragment.java | 120 ++++++------ .../apps/abit/ImportIdentitiesFragment.java | 26 ++- .../ch/dissem/apps/abit/MainActivity.java | 13 +- .../repository/AndroidAddressRepository.java | 174 ++++++++++++------ .../repository/AndroidMessageRepository.java | 34 ++-- .../abit/repository/AndroidNodeRegistry.java | 8 +- .../AndroidProofOfWorkRepository.java | 19 +- .../apps/abit/service/BitmessageService.java | 2 +- .../dissem/apps/abit/service/Singleton.java | 8 +- .../abit/synchronization/SyncAdapter.java | 4 +- .../ch/dissem/apps/abit/util/Drawables.java | 8 +- .../ch/dissem/apps/abit/util/UuidUtils.java | 6 +- .../main/res/drawable/avatar_placeholder.xml | 12 ++ app/src/main/res/layout/subscription_row.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- build.gradle | 2 +- 17 files changed, 261 insertions(+), 182 deletions(-) create mode 100644 app/src/main/res/drawable/avatar_placeholder.xml diff --git a/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java index 9dec757..791e1b2 100644 --- a/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java @@ -70,9 +70,6 @@ public class AddressDetailFragment extends Fragment { super.onCreate(savedInstanceState); if (getArguments().containsKey(ARG_ITEM)) { - // Load the dummy content specified by the fragment - // arguments. In a real-world scenario, use a Loader - // to load content from a content provider. item = (BitmessageAddress) getArguments().getSerializable(ARG_ITEM); } setHasOptionsMenu(true); diff --git a/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java b/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java index 93e1f2e..9633cbe 100644 --- a/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java @@ -19,6 +19,7 @@ package ch.dissem.apps.abit; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -32,11 +33,11 @@ import android.widget.TextView; import com.google.zxing.integration.android.IntentIntegrator; -import java.util.Collections; -import java.util.Comparator; +import java.util.LinkedList; import java.util.List; import ch.dissem.apps.abit.listener.ActionBarListener; +import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.valueobject.Label; @@ -47,6 +48,46 @@ import io.github.yavski.fabspeeddial.SimpleMenuListenerAdapter; * Fragment that shows a list of all contacts, the ones we subscribed to first. */ public class AddressListFragment extends AbstractItemListFragment { + private ArrayAdapter adapter; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + adapter = new ArrayAdapter( + getActivity(), + R.layout.subscription_row, + R.id.name, + new LinkedList()) { + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + ViewHolder v; + if (convertView == null) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + convertView = inflater.inflate(R.layout.subscription_row, parent, false); + v = new ViewHolder(); + v.ctx = getContext(); + v.avatar = (ImageView) convertView.findViewById(R.id.avatar); + v.name = (TextView) convertView.findViewById(R.id.name); + v.streamNumber = (TextView) convertView.findViewById(R.id.stream_number); + v.subscribed = convertView.findViewById(R.id.subscribed); + convertView.setTag(v); + } else { + v = (ViewHolder) convertView.getTag(); + } + BitmessageAddress item = getItem(position); + assert item != null; + v.avatar.setImageDrawable(new Identicon(item)); + v.name.setText(item.toString()); + v.streamNumber.setText(v.ctx.getString(R.string.stream_number, item.getStream())); + v.subscribed.setVisibility(item.isSubscribed() ? View.VISIBLE : View.INVISIBLE); + return convertView; + } + }; + setListAdapter(adapter); + } + @Override public void onResume() { super.onResume(); @@ -55,61 +96,26 @@ public class AddressListFragment extends AbstractItemListFragment addresses = Singleton.getAddressRepository(getContext()) - .getContacts(); - Collections.sort(addresses, new Comparator() { + adapter.clear(); + final AndroidAddressRepository addressRepo = Singleton.getAddressRepository(getContext()); + new AsyncTask() { @Override - public int compare(BitmessageAddress lhs, BitmessageAddress rhs) { - // Yields the following order: - // * Subscribed addresses come first - // * Addresses with Aliases (alphabetically) - // * Addresses (alphabetically) - if (lhs.isSubscribed() == rhs.isSubscribed()) { - if (lhs.getAlias() != null) { - if (rhs.getAlias() != null) { - return lhs.getAlias().compareTo(rhs.getAlias()); - } else { - return -1; - } - } else if (rhs.getAlias() != null) { - return 1; - } else { - return lhs.getAddress().compareTo(rhs.getAddress()); - } + protected Void doInBackground(Void... params) { + List ids = addressRepo.getContactIds(); + for (String id : ids) { + BitmessageAddress address = addressRepo.getById(id); + publishProgress(address); } - if (lhs.isSubscribed()) { - return -1; - } else { - return 1; + return null; + } + + @Override + protected void onProgressUpdate(BitmessageAddress... values) { + for (BitmessageAddress address : values) { + adapter.add(address); } } - }); - setListAdapter(new ArrayAdapter( - getActivity(), - android.R.layout.simple_list_item_activated_1, - android.R.id.text1, - addresses) { - @NonNull - @Override - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - if (convertView == null) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - convertView = inflater.inflate(R.layout.subscription_row, parent, false); - } - BitmessageAddress item = getItem(position); - assert item != null; - ((ImageView) convertView.findViewById(R.id.avatar)).setImageDrawable(new - Identicon(item)); - TextView name = (TextView) convertView.findViewById(R.id.name); - name.setText(item.toString()); - TextView streamNumber = (TextView) convertView.findViewById(R.id.stream_number); - streamNumber.setText(getContext().getString(R.string.stream_number, - item.getStream())); - convertView.findViewById(R.id.subscribed).setVisibility(item.isSubscribed() ? - View.VISIBLE : View.INVISIBLE); - return convertView; - } - }); + }.execute(); } @Override @@ -163,4 +169,12 @@ public class AddressListFragment extends AbstractItemListFragment getIdentities() { return find("private_key IS NOT NULL"); } + @NonNull @Override public List getChans() { return find("chan = '1'"); } + @NonNull @Override public List getSubscriptions() { return find("subscribed = '1'"); } + @NonNull @Override public List getSubscriptions(long broadcastVersion) { if (broadcastVersion > 4) { @@ -108,11 +113,55 @@ public class AndroidAddressRepository implements AddressRepository { } } + @NonNull @Override public List getContacts() { return find("private_key IS NULL OR chan = '1'"); } + /** + * Returns the contacts in the following order: + *
    + *
  • Subscribed addresses come first + *
  • Addresses with Aliases (alphabetically) + *
  • Addresses (alphabetically) + *
+ * + * @return the ordered list of ids (address strings) + */ + @NonNull + public List getContactIds() { + return findIds( + "private_key IS NULL OR chan = '1'", + COLUMN_SUBSCRIBED + " DESC, " + COLUMN_ALIAS + " IS NULL, " + COLUMN_ALIAS + ", " + COLUMN_ADDRESS + ); + } + + @NonNull + private List findIds(String where, String orderBy) { + List result = new LinkedList<>(); + + // Define a projection that specifies which columns from the database + // you will actually use after this query. + String[] projection = { + COLUMN_ADDRESS + }; + + SQLiteDatabase db = sql.getReadableDatabase(); + try (Cursor c = db.query( + TABLE_NAME, projection, + where, + null, null, null, + orderBy + )) { + while (c.moveToNext()) { + result.add(c.getString(c.getColumnIndex(COLUMN_ADDRESS))); + } + } + return result; + } + + @NonNull private List find(String where) { List result = new LinkedList<>(); @@ -161,8 +210,6 @@ public class AndroidAddressRepository implements AddressRepository { result.add(address); } - } catch (IOException e) { - LOG.error(e.getMessage(), e); } return result; } @@ -188,61 +235,55 @@ public class AndroidAddressRepository implements AddressRepository { } private void update(BitmessageAddress address) { - try { - SQLiteDatabase db = sql.getWritableDatabase(); - // Create a new map of values, where column names are the keys - ContentValues values = new ContentValues(); - if (address.getAlias() != null) { - values.put(COLUMN_ALIAS, address.getAlias()); - } - if (address.getPubkey() != null) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - address.getPubkey().writeUnencrypted(out); - values.put(COLUMN_PUBLIC_KEY, out.toByteArray()); - } - if (address.getPrivateKey() != null) { - values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey())); - } - if (address.isChan()) { - values.put(COLUMN_CHAN, true); - } - values.put(COLUMN_SUBSCRIBED, address.isSubscribed()); + SQLiteDatabase db = sql.getWritableDatabase(); + // Create a new map of values, where column names are the keys + ContentValues values = new ContentValues(); + if (address.getAlias() != null) { + values.put(COLUMN_ALIAS, address.getAlias()); + } + if (address.getPubkey() != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + address.getPubkey().writeUnencrypted(out); + values.put(COLUMN_PUBLIC_KEY, out.toByteArray()); + } + if (address.getPrivateKey() != null) { + values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey())); + } + if (address.isChan()) { + values.put(COLUMN_CHAN, true); + } + values.put(COLUMN_SUBSCRIBED, address.isSubscribed()); - int update = db.update(TABLE_NAME, values, "address=?", - new String[]{address.getAddress()}); - if (update < 0) { - LOG.error("Could not update address " + address); - } - } catch (IOException e) { - LOG.error(e.getMessage(), e); + int update = db.update(TABLE_NAME, values, "address=?", + new String[]{address.getAddress()}); + if (update < 0) { + LOG.error("Could not update address " + address); } } private void insert(BitmessageAddress address) { - try { - SQLiteDatabase db = sql.getWritableDatabase(); - // Create a new map of values, where column names are the keys - ContentValues values = new ContentValues(); - values.put(COLUMN_ADDRESS, address.getAddress()); - values.put(COLUMN_VERSION, address.getVersion()); - values.put(COLUMN_ALIAS, address.getAlias()); - if (address.getPubkey() != null) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - address.getPubkey().writeUnencrypted(out); - values.put(COLUMN_PUBLIC_KEY, out.toByteArray()); - } else { - values.put(COLUMN_PUBLIC_KEY, (byte[]) null); - } + SQLiteDatabase db = sql.getWritableDatabase(); + // Create a new map of values, where column names are the keys + ContentValues values = new ContentValues(); + values.put(COLUMN_ADDRESS, address.getAddress()); + values.put(COLUMN_VERSION, address.getVersion()); + values.put(COLUMN_ALIAS, address.getAlias()); + if (address.getPubkey() != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + address.getPubkey().writeUnencrypted(out); + values.put(COLUMN_PUBLIC_KEY, out.toByteArray()); + } else { + values.put(COLUMN_PUBLIC_KEY, (byte[]) null); + } + if (address.getPrivateKey() != null) { values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey())); - values.put(COLUMN_CHAN, address.isChan()); - values.put(COLUMN_SUBSCRIBED, address.isSubscribed()); + } + values.put(COLUMN_CHAN, address.isChan()); + values.put(COLUMN_SUBSCRIBED, address.isSubscribed()); - long insert = db.insert(TABLE_NAME, null, values); - if (insert < 0) { - LOG.error("Could not insert address " + address); - } - } catch (IOException e) { - LOG.error(e.getMessage(), e); + long insert = db.insert(TABLE_NAME, null, values); + if (insert < 0) { + LOG.error("Could not insert address " + address); } } @@ -252,10 +293,21 @@ public class AndroidAddressRepository implements AddressRepository { db.delete(TABLE_NAME, "address = ?", new String[]{address.getAddress()}); } + @NonNull + public BitmessageAddress getById(String id) { + List result = find("address = '" + id + "'"); + if (result.size() > 0) { + return result.get(0); + } else { + throw new ApplicationException("Address with id " + id + " not found."); + } + } + + @NonNull @Override public BitmessageAddress getAddress(String address) { List result = find("address = '" + address + "'"); if (result.size() > 0) return result.get(0); - return null; + return new BitmessageAddress(address); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java index 8d44495..b62e761 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidMessageRepository.java @@ -22,12 +22,12 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -91,6 +91,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository { this.context = ctx; } + @NonNull @Override public List findMessages(Label label) { if (label == LABEL_ARCHIVE) { @@ -100,6 +101,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository { } } + @NonNull public List<Label> findLabels(String where) { List<Label> result = new LinkedList<>(); @@ -156,7 +158,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository { } else { where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=?) AND "; args = new String[]{ - label.getId().toString(), + String.valueOf(label.getId()), Label.Type.UNREAD.name() }; } @@ -168,6 +170,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository { ); } + @NonNull @Override public List<UUID> findConversations(Label label) { String[] projection = { @@ -202,20 +205,22 @@ public class AndroidMessageRepository extends AbstractMessageRepository { return; } byte[] childIV = message.getInventoryVector().getHash(); - db.delete(PARENTS_TABLE_NAME, "child=?", new String[]{hex(childIV).toString()}); + db.delete(PARENTS_TABLE_NAME, "child=?", new String[]{hex(childIV)}); // save new parents int order = 0; for (InventoryVector parentIV : message.getParents()) { Plaintext parent = getMessage(parentIV); - mergeConversations(db, parent.getConversationId(), message.getConversationId()); - order++; - ContentValues values = new ContentValues(); - values.put("parent", parentIV.getHash()); - values.put("child", childIV); - values.put("pos", order); - values.put("conversation", UuidUtils.asBytes(message.getConversationId())); - db.insertOrThrow(PARENTS_TABLE_NAME, null, values); + if (parent != null) { + mergeConversations(db, parent.getConversationId(), message.getConversationId()); + order++; + ContentValues values = new ContentValues(); + values.put("parent", parentIV.getHash()); + values.put("child", childIV); + values.put("pos", order); + values.put("conversation", UuidUtils.asBytes(message.getConversationId())); + db.insertOrThrow(PARENTS_TABLE_NAME, null, values); + } } } @@ -229,11 +234,12 @@ public class AndroidMessageRepository extends AbstractMessageRepository { private void mergeConversations(SQLiteDatabase db, UUID source, UUID target) { ContentValues values = new ContentValues(); values.put("conversation", UuidUtils.asBytes(target)); - String[] whereArgs = {hex(UuidUtils.asBytes(source)).toString()}; + String[] whereArgs = {hex(UuidUtils.asBytes(source))}; db.update(TABLE_NAME, values, "conversation=?", whereArgs); db.update(PARENTS_TABLE_NAME, values, "conversation=?", whereArgs); } + @NonNull protected List<Plaintext> find(String where) { List<Plaintext> result = new LinkedList<>(); @@ -292,8 +298,6 @@ public class AndroidMessageRepository extends AbstractMessageRepository { builder.labels(findLabels(id)); result.add(builder.build()); } - } catch (IOException e) { - LOG.error(e.getMessage(), e); } return result; } @@ -348,7 +352,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository { values.put(COLUMN_ACK_DATA, message.getAckData()); values.put(COLUMN_SENT, message.getSent()); values.put(COLUMN_RECEIVED, message.getReceived()); - values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name()); + values.put(COLUMN_STATUS, message.getStatus().name()); values.put(COLUMN_INITIAL_HASH, message.getInitialHash()); values.put(COLUMN_TTL, message.getTTL()); values.put(COLUMN_RETRIES, message.getRetries()); diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidNodeRegistry.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidNodeRegistry.java index 1ab7d5c..2722ca1 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidNodeRegistry.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidNodeRegistry.java @@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDoneException; import android.database.sqlite.SQLiteStatement; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,7 +54,7 @@ public class AndroidNodeRegistry implements NodeRegistry { private void cleanUp() { SQLiteDatabase db = sql.getWritableDatabase(); - db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))}); + db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now() - 28 * DAY)}); } @Override @@ -82,6 +83,7 @@ public class AndroidNodeRegistry implements NodeRegistry { } } + @NonNull @Override public List<NetworkAddress> getKnownAddresses(int limit, long... streams) { String[] projection = { @@ -97,7 +99,7 @@ public class AndroidNodeRegistry implements NodeRegistry { try (Cursor c = db.query( TABLE_NAME, projection, "stream IN (?)", - new String[]{SqlStrings.join(streams).toString()}, + new String[]{SqlStrings.join(streams)}, null, null, "time DESC", valueOf(limit) @@ -140,7 +142,7 @@ public class AndroidNodeRegistry implements NodeRegistry { try { cleanUp(); for (NetworkAddress node : nodes) { - if (node.getTime() < now(+5 * MINUTE) && node.getTime() > now(-28 * DAY)) { + if (node.getTime() < now() + 5 * MINUTE && node.getTime() > now() - 28 * DAY) { synchronized (this) { Long existing = loadExistingTime(node); if (existing == null) { diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidProofOfWorkRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidProofOfWorkRepository.java index 6b944d2..95cbc5e 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidProofOfWorkRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidProofOfWorkRepository.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +66,7 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte this.bmc = internalContext; } + @NonNull @Override public Item getItem(byte[] initialHash) { // Define a projection that specifies which columns from the database @@ -111,6 +113,7 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte hex(initialHash)); } + @NonNull @Override public List<byte[]> getItems() { // Define a projection that specifies which columns from the database @@ -139,14 +142,14 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte 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, cryptography().getInitialHash(item.object)); - values.put(COLUMN_DATA, Encode.bytes(item.object)); - values.put(COLUMN_VERSION, item.object.getVersion()); - values.put(COLUMN_NONCE_TRIALS_PER_BYTE, item.nonceTrialsPerByte); - values.put(COLUMN_EXTRA_BYTES, item.extraBytes); - if (item.message != null) { - values.put(COLUMN_EXPIRATION_TIME, item.expirationTime); - values.put(COLUMN_MESSAGE_ID, (Long) item.message.getId()); + values.put(COLUMN_INITIAL_HASH, cryptography().getInitialHash(item.getObjectMessage())); + values.put(COLUMN_DATA, Encode.bytes(item.getObjectMessage())); + values.put(COLUMN_VERSION, item.getObjectMessage().getVersion()); + values.put(COLUMN_NONCE_TRIALS_PER_BYTE, item.getNonceTrialsPerByte()); + values.put(COLUMN_EXTRA_BYTES, item.getExtraBytes()); + if (item.getMessage() != null) { + values.put(COLUMN_EXPIRATION_TIME, item.getExpirationTime()); + values.put(COLUMN_MESSAGE_ID, (Long) item.getMessage().getId()); } db.insertOrThrow(TABLE_NAME, null, values); diff --git a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java index ef64b61..0b33907 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java @@ -100,7 +100,7 @@ public class BitmessageService extends Service { if (bmc != null) { return bmc.status(); } else { - return new Property("bitmessage context", null); + return new Property("bitmessage context"); } } } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index e01f099..64c6ff7 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -99,12 +99,12 @@ public class Singleton { return messageListener; } - public static MessageRepository getMessageRepository(Context ctx) { - return getBitmessageContext(ctx).messages(); + public static AndroidMessageRepository getMessageRepository(Context ctx) { + return (AndroidMessageRepository) getBitmessageContext(ctx).messages(); } - public static AddressRepository getAddressRepository(Context ctx) { - return getBitmessageContext(ctx).addresses(); + public static AndroidAddressRepository getAddressRepository(Context ctx) { + return (AndroidAddressRepository) getBitmessageContext(ctx).addresses(); } public static ProofOfWorkRepository getProofOfWorkRepository(Context ctx) { diff --git a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java index 770adc0..c0a3655 100644 --- a/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java +++ b/app/src/main/java/ch/dissem/apps/abit/synchronization/SyncAdapter.java @@ -109,6 +109,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { LOG.info("Looking for completed POW"); BitmessageAddress identity = Singleton.getIdentity(getContext()); + @SuppressWarnings("ConstantConditions") byte[] privateKey = identity.getPrivateKey().getPrivateEncryptionKey(); byte[] signingKey = cryptography().createPublicKey(identity.getPublicDecryptionKey()); ProofOfWorkRequest.Reader reader = new ProofOfWorkRequest.Reader(identity); @@ -116,8 +117,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { List<byte[]> items = powRepo.getItems(); for (byte[] initialHash : items) { ProofOfWorkRepository.Item item = powRepo.getItem(initialHash); - byte[] target = cryptography().getProofOfWorkTarget(item.object, item - .nonceTrialsPerByte, item.extraBytes); + byte[] target = cryptography().getProofOfWorkTarget(item.getObjectMessage(), item.getNonceTrialsPerByte(), item.getExtraBytes()); CryptoCustomMessage<ProofOfWorkRequest> cryptoMsg = new CryptoCustomMessage<>( new ProofOfWorkRequest(identity, initialHash, CALCULATE, target)); cryptoMsg.signAndEncrypt(identity, signingKey); diff --git a/app/src/main/java/ch/dissem/apps/abit/util/Drawables.java b/app/src/main/java/ch/dissem/apps/abit/util/Drawables.java index 721cc87..4c28184 100644 --- a/app/src/main/java/ch/dissem/apps/abit/util/Drawables.java +++ b/app/src/main/java/ch/dissem/apps/abit/util/Drawables.java @@ -34,12 +34,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; -import java.io.IOException; import ch.dissem.apps.abit.Identicon; import ch.dissem.apps.abit.R; import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.exception.ApplicationException; import static android.graphics.Color.BLACK; import static android.graphics.Color.WHITE; @@ -83,11 +81,7 @@ public class Drawables { if (address.getPubkey() != null) { link.append(address.getAlias() == null ? '?' : '&'); ByteArrayOutputStream pubkey = new ByteArrayOutputStream(); - try { - address.getPubkey().writeUnencrypted(pubkey); - } catch (IOException e) { - throw new ApplicationException(e); - } + address.getPubkey().writeUnencrypted(pubkey); link.append("pubkey=").append(Base64.encodeToString(pubkey.toByteArray(), URL_SAFE | NO_WRAP)); } BitMatrix result; diff --git a/app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java b/app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java index 543dbac..c25a583 100644 --- a/app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java +++ b/app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java @@ -15,9 +15,13 @@ import java.util.UUID; * </p> */ public class UuidUtils { + /** + * @param bytes that represent a UUID, or null for a random UUID + * @return the UUID from the given bytes, or a random UUID if bytes is null. + */ public static UUID asUuid(byte[] bytes) { if (bytes == null) { - return null; + return UUID.randomUUID(); } ByteBuffer bb = ByteBuffer.wrap(bytes); long firstLong = bb.getLong(); diff --git a/app/src/main/res/drawable/avatar_placeholder.xml b/app/src/main/res/drawable/avatar_placeholder.xml new file mode 100644 index 0000000..9b5bd5d --- /dev/null +++ b/app/src/main/res/drawable/avatar_placeholder.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid + android:color="@color/colorAccent"/> + + <size + android:width="40dp" + android:height="40dp"/> +</shape> diff --git a/app/src/main/res/layout/subscription_row.xml b/app/src/main/res/layout/subscription_row.xml index 8a40938..83ef228 100644 --- a/app/src/main/res/layout/subscription_row.xml +++ b/app/src/main/res/layout/subscription_row.xml @@ -29,7 +29,7 @@ android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_margin="16dp" - android:src="@color/colorAccent" + android:src="@drawable/avatar_placeholder" tools:ignore="ContentDescription"/> <TextView diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac64a6d..85ae6b0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -65,7 +65,7 @@ <string name="pubkey_available">Public key available</string> <string name="pubkey_not_available">Public key not yet available</string> <string name="alt_qr_code">QR code</string> - <string name="add_identity_warning">Having more identities will reequire more resources. If you are sure you want to add an identity, please select what exactly you want to do:</string> + <string name="add_identity_warning">Having more identities will require more resources. If you are sure you want to add an identity, please select what exactly you want to do:</string> <string name="share">Share</string> <string name="delete_identity_warning">Are you sure you want to delete this identity? You won\'t be able to receive any messages sent to this address and can\'t undo this operation.</string> <string name="delete_contact_warning">Are you sure you want to delete this contact?</string> diff --git a/build.gradle b/build.gradle index 51bb49b..b06c3d3 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.github.ben-manes:gradle-versions-plugin:0.14.0' // NOTE: Do not place your application dependencies here; they belong