Asynchronously load contacts to improve responsiveness
This commit is contained in:
parent
a67560c28b
commit
bf52d2f3de
@ -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);
|
||||
|
@ -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<BitmessageAddress> {
|
||||
private ArrayAdapter<BitmessageAddress> adapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
adapter = new ArrayAdapter<BitmessageAddress>(
|
||||
getActivity(),
|
||||
R.layout.subscription_row,
|
||||
R.id.name,
|
||||
new LinkedList<BitmessageAddress>()) {
|
||||
@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<BitmessageAddr
|
||||
}
|
||||
|
||||
public void updateList() {
|
||||
List<BitmessageAddress> addresses = Singleton.getAddressRepository(getContext())
|
||||
.getContacts();
|
||||
Collections.sort(addresses, new Comparator<BitmessageAddress>() {
|
||||
adapter.clear();
|
||||
final AndroidAddressRepository addressRepo = Singleton.getAddressRepository(getContext());
|
||||
new AsyncTask<Void, BitmessageAddress, Void>() {
|
||||
@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<String> 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<BitmessageAddress>(
|
||||
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<BitmessageAddr
|
||||
public void updateList(Label label) {
|
||||
updateList();
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
private Context ctx;
|
||||
private ImageView avatar;
|
||||
private TextView name;
|
||||
private TextView streamNumber;
|
||||
private View subscribed;
|
||||
}
|
||||
}
|
||||
|
@ -52,21 +52,19 @@ public class ImportIdentitiesFragment extends Fragment {
|
||||
String wifData = getArguments().getString(WIF_DATA);
|
||||
BitmessageContext bmc = Singleton.getBitmessageContext(getActivity());
|
||||
View view = inflater.inflate(R.layout.fragment_import_select_identities, container, false);
|
||||
try {
|
||||
importer = new WifImporter(bmc, wifData);
|
||||
adapter = new AddressSelectorAdapter(importer.getIdentities());
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(),
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false);
|
||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
recyclerView.addItemDecoration(new SimpleListDividerDecorator(
|
||||
ContextCompat.getDrawable(getActivity(), R.drawable.list_divider_h), true));
|
||||
} catch (IOException e) {
|
||||
return super.onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
importer = new WifImporter(bmc, wifData);
|
||||
adapter = new AddressSelectorAdapter(importer.getIdentities());
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(),
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false);
|
||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
recyclerView.addItemDecoration(new SimpleListDividerDecorator(
|
||||
ContextCompat.getDrawable(getActivity(), R.drawable.list_divider_h), true));
|
||||
|
||||
view.findViewById(R.id.finish).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
@ -499,14 +499,13 @@ public class MainActivity extends AppCompatActivity
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putSerializable(MessageDetailFragment.ARG_ITEM, item);
|
||||
Fragment fragment;
|
||||
if (item instanceof Plaintext)
|
||||
if (item instanceof Plaintext) {
|
||||
fragment = new MessageDetailFragment();
|
||||
else if (item instanceof BitmessageAddress)
|
||||
} else if (item instanceof String) {
|
||||
fragment = new AddressDetailFragment();
|
||||
else
|
||||
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
|
||||
"was "
|
||||
+ item.getClass().getSimpleName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but was " + item.getClass().getSimpleName());
|
||||
}
|
||||
fragment.setArguments(arguments);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.message_detail_container, fragment)
|
||||
@ -518,7 +517,7 @@ public class MainActivity extends AppCompatActivity
|
||||
if (item instanceof Plaintext) {
|
||||
detailIntent = new Intent(this, MessageDetailActivity.class);
|
||||
detailIntent.putExtra(EXTRA_SHOW_LABEL, selectedLabel);
|
||||
} else if (item instanceof BitmessageAddress) {
|
||||
} else if (item instanceof String) {
|
||||
detailIntent = new Intent(this, AddressDetailActivity.class);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
|
||||
|
@ -19,26 +19,27 @@ package ch.dissem.apps.abit.repository;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
/**
|
||||
* {@link AddressRepository} implementation using the Android SQL API.
|
||||
*/
|
||||
@ -84,21 +85,25 @@ public class AndroidAddressRepository implements AddressRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<BitmessageAddress> getIdentities() {
|
||||
return find("private_key IS NOT NULL");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<BitmessageAddress> getChans() {
|
||||
return find("chan = '1'");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<BitmessageAddress> getSubscriptions() {
|
||||
return find("subscribed = '1'");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<BitmessageAddress> getSubscriptions(long broadcastVersion) {
|
||||
if (broadcastVersion > 4) {
|
||||
@ -108,11 +113,55 @@ public class AndroidAddressRepository implements AddressRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<BitmessageAddress> getContacts() {
|
||||
return find("private_key IS NULL OR chan = '1'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contacts in the following order:
|
||||
* <ul>
|
||||
* <li>Subscribed addresses come first
|
||||
* <li>Addresses with Aliases (alphabetically)
|
||||
* <li>Addresses (alphabetically)
|
||||
* </ul>
|
||||
*
|
||||
* @return the ordered list of ids (address strings)
|
||||
*/
|
||||
@NonNull
|
||||
public List<String> getContactIds() {
|
||||
return findIds(
|
||||
"private_key IS NULL OR chan = '1'",
|
||||
COLUMN_SUBSCRIBED + " DESC, " + COLUMN_ALIAS + " IS NULL, " + COLUMN_ALIAS + ", " + COLUMN_ADDRESS
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<String> findIds(String where, String orderBy) {
|
||||
List<String> 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<BitmessageAddress> find(String where) {
|
||||
List<BitmessageAddress> 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<BitmessageAddress> 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<BitmessageAddress> result = find("address = '" + address + "'");
|
||||
if (result.size() > 0) return result.get(0);
|
||||
return null;
|
||||
return new BitmessageAddress(address);
|
||||
}
|
||||
}
|
||||
|
@ -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<Plaintext> 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());
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
12
app/src/main/res/drawable/avatar_placeholder.xml
Normal file
12
app/src/main/res/drawable/avatar_placeholder.xml
Normal file
@ -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>
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user