Minor bug fixes and improvements, added caching to inventory

This commit is contained in:
Christian Basler 2016-01-19 07:43:48 +01:00
parent 6ec25cfae7
commit 6f6a2e4e6c
9 changed files with 186 additions and 101 deletions

View File

@ -188,6 +188,16 @@ public class MainActivity extends AppCompatActivity
.withTag(identity)
);
}
if (profiles.isEmpty()){
// Create an initial identity
BitmessageAddress identity = Singleton.getIdentity(this);
profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity))
.withName(identity.toString())
.withEmail(identity.getAddress())
.withTag(identity)
);
}
profiles.add(new ProfileSettingDrawerItem()
.withName("Add Identity")
.withDescription("Create new identity")

View File

@ -100,7 +100,8 @@ public class SubscriptionDetailFragment extends Fragment {
TextView address = (TextView) rootView.findViewById(R.id.address);
address.setText(item.getAddress());
address.setSelected(true);
((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity().getString(R.string.stream_number, item.getStream()));
((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity()
.getString(R.string.stream_number, item.getStream()));
Switch active = (Switch) rootView.findViewById(R.id.active);
active.setChecked(item.isSubscribed());
active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@ -109,6 +110,14 @@ public class SubscriptionDetailFragment extends Fragment {
item.setSubscribed(isChecked);
}
});
ImageView pubkeyAvailableImg = (ImageView) rootView.findViewById(R.id.pubkey_available);
if (item.getPubkey() == null) {
pubkeyAvailableImg.setAlpha(0.3f);
TextView pubkeyAvailableDesc = (TextView) rootView.findViewById(R.id
.pubkey_available_desc);
pubkeyAvailableDesc.setText(R.string.pubkey_not_available);
}
}
return rootView;

View File

@ -33,10 +33,18 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static ch.dissem.apps.abit.repository.SqlHelper.join;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static ch.dissem.bitmessage.utils.UnixTime.now;
/**
@ -55,47 +63,64 @@ public class AndroidInventory implements Inventory {
private final SqlHelper sql;
private final Map<Long, Map<InventoryVector, Long>> cache = new ConcurrentHashMap<>();
public AndroidInventory(SqlHelper sql) {
this.sql = sql;
}
@Override
public List<InventoryVector> getInventory(long... streams) {
return getInventory(false, streams);
List<InventoryVector> result = new LinkedList<>();
long now = now();
for (long stream : streams) {
for (Map.Entry<InventoryVector, Long> e : getCache(stream).entrySet()) {
if (e.getValue() > now) {
result.add(e.getKey());
}
}
}
return result;
}
public List<InventoryVector> getInventory(boolean includeExpired, long... streams) {
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
COLUMN_HASH
};
private Map<InventoryVector, Long> getCache(long stream) {
Map<InventoryVector, Long> result = cache.get(stream);
if (result == null) {
synchronized (cache) {
if (cache.get(stream) == null) {
result = new ConcurrentHashMap<>();
cache.put(stream, result);
SQLiteDatabase db = sql.getReadableDatabase();
Cursor c = db.query(
TABLE_NAME, projection,
(includeExpired ? "" : "expires > " + now() + " AND ") + "stream IN (" + join
(streams) + ")",
null, null, null, null
);
List<InventoryVector> result = new LinkedList<>();
try {
c.moveToFirst();
while (!c.isAfterLast()) {
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
result.add(new InventoryVector(blob));
c.moveToNext();
String[] projection = {
COLUMN_HASH, COLUMN_EXPIRES
};
SQLiteDatabase db = sql.getReadableDatabase();
try (Cursor c = db.query(
TABLE_NAME, projection,
"stream = " + stream,
null, null, null, null
)) {
c.moveToFirst();
while (!c.isAfterLast()) {
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
long expires = c.getLong(c.getColumnIndex(COLUMN_EXPIRES));
result.put(new InventoryVector(blob), expires);
c.moveToNext();
}
}
LOG.info("Stream #" + stream + " inventory size: " + result.size());
}
}
} finally {
c.close();
}
LOG.info("Inventory size: " + result.size());
return result;
}
@Override
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
offer.removeAll(getInventory(true, streams));
for (long stream : streams) {
offer.removeAll(getCache(stream).keySet());
}
LOG.info(offer.size() + " objects missing.");
return offer;
}
@ -110,12 +135,11 @@ public class AndroidInventory implements Inventory {
};
SQLiteDatabase db = sql.getReadableDatabase();
Cursor c = db.query(
try (Cursor c = db.query(
TABLE_NAME, projection,
"hash = X'" + vector + "'",
null, null, null, null
);
try {
)) {
c.moveToFirst();
if (c.isAfterLast()) {
LOG.info("Object requested that we don't have. IV: " + vector);
@ -125,8 +149,6 @@ public class AndroidInventory implements Inventory {
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
return Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob.length);
} finally {
c.close();
}
}
@ -150,13 +172,12 @@ public class AndroidInventory implements Inventory {
}
SQLiteDatabase db = sql.getReadableDatabase();
Cursor c = db.query(
List<ObjectMessage> result = new LinkedList<>();
try (Cursor c = db.query(
TABLE_NAME, projection,
where.toString(),
null, null, null, null
);
List<ObjectMessage> result = new LinkedList<>();
try {
)) {
c.moveToFirst();
while (!c.isAfterLast()) {
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
@ -165,8 +186,6 @@ public class AndroidInventory implements Inventory {
blob.length));
c.moveToNext();
}
} finally {
c.close();
}
return result;
}
@ -174,6 +193,10 @@ public class AndroidInventory implements Inventory {
@Override
public void storeObject(ObjectMessage object) {
InventoryVector iv = object.getInventoryVector();
if (getCache(object.getStream()).containsKey(iv))
return;
LOG.trace("Storing object " + iv);
try {
@ -188,6 +211,8 @@ public class AndroidInventory implements Inventory {
values.put(COLUMN_VERSION, object.getVersion());
db.insertOrThrow(TABLE_NAME, null, values);
getCache(object.getStream()).put(iv, object.getExpiresTime());
} catch (SQLiteConstraintException e) {
LOG.trace(e.getMessage(), e);
} catch (IOException e) {
@ -197,22 +222,22 @@ public class AndroidInventory implements Inventory {
@Override
public boolean contains(ObjectMessage object) {
SQLiteDatabase db = sql.getReadableDatabase();
Cursor c = db.query(
TABLE_NAME, new String[]{COLUMN_STREAM},
"hash = X'" + object.getInventoryVector() + "'",
null, null, null, null
);
try {
return c.getCount() > 0;
} finally {
c.close();
}
return getCache(object.getStream()).keySet().contains(object.getInventoryVector());
}
@Override
public void cleanup() {
long fiveMinutesAgo = now() - 5 * MINUTE;
SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, "expires < " + (now() - 300), null);
db.delete(TABLE_NAME, "expires < " + fiveMinutesAgo, null);
for (Map<InventoryVector, Long> c : cache.values()) {
Iterator<Map.Entry<InventoryVector, Long>> iterator = c.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue() < fiveMinutesAgo) {
iterator.remove();
}
}
}
}
}

View File

@ -56,7 +56,7 @@ public class BitmessageService extends Service {
private static Messenger messenger;
public static boolean isRunning() {
return running;
return running && bmc.isRunning();
}
@Override
@ -102,8 +102,6 @@ public class BitmessageService extends Service {
switch (msg.what) {
case MSG_CREATE_IDENTITY: {
BitmessageAddress identity = bmc.createIdentity(false);
identity.setAlias(service.get().getString(R.string.alias_default_identity));
bmc.addresses().save(identity);
if (msg.replyTo != null) {
try {
Message message = Message.obtain(this, MSG_CREATE_IDENTITY);

View File

@ -4,6 +4,7 @@ import android.content.Context;
import java.util.List;
import ch.dissem.apps.abit.R;
import ch.dissem.apps.abit.adapter.AndroidCryptography;
import ch.dissem.apps.abit.adapter.SwitchingProofOfWorkEngine;
import ch.dissem.apps.abit.listener.MessageListener;
@ -92,10 +93,15 @@ public class Singleton {
if (identity == null) {
synchronized (Singleton.class) {
if (identity == null) {
List<BitmessageAddress> identities = getBitmessageContext(ctx).addresses()
BitmessageContext bmc = getBitmessageContext(ctx);
List<BitmessageAddress> identities = bmc.addresses()
.getIdentities();
if (identities.size() > 0) {
identity = identities.get(0);
} else {
identity = bmc.createIdentity(false);
identity.setAlias(ctx.getString(R.string.alias_default_identity));
bmc.addresses().save(identity);
}
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,0 14,15A2,2 0 0,0 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17Z" />
</vector>

View File

@ -16,65 +16,90 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:src="@color/colorAccent"
android:layout_margin="16dp"/>
android:id="@+id/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_margin="16dp"
android:src="@color/colorAccent"
tools:ignore="ContentDescription"/>
<EditText
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Name"
android:layout_alignTop="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/avatar"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@+id/avatar"
android:text=""
android:inputType="textPersonName"
tools:ignore="LabelFor"/>
<TextView
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Address"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_below="@+id/avatar"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textStyle="bold"
/>
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/avatar"
android:lines="1"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="BM-XyYxXyYxXyYxXyYxXyYx"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold"
tools:ignore="HardcodedText"/>
<TextView
android:id="@+id/stream_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stream #"
android:lines="1"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_below="@+id/address"/>
android:id="@+id/stream_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/address"
android:ellipsize="end"
android:lines="1"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="Stream #"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:ignore="HardcodedText"/>
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/subscribed"
android:id="@+id/active"
android:paddingTop="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_below="@+id/stream_number"/>
android:id="@+id/active"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/stream_number"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:text="@string/subscribed"/>
<ImageView
android:id="@+id/pubkey_available"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/active"
android:paddingStart="16dp"
android:paddingEnd="4dp"
android:paddingTop="16dp"
android:src="@drawable/public_key"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/pubkey_available_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="0dp"
android:paddingEnd="16dp"
android:layout_alignBottom="@id/pubkey_available"
android:layout_toEndOf="@id/pubkey_available"
android:layout_alignParentEnd="true"
android:text="@string/pubkey_available"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</RelativeLayout>

View File

@ -56,4 +56,6 @@
<string name="alias_default_identity">Ich</string>
<string name="title_activity_status">Debugging</string>
<string name="status_summary">Technische Infos</string>
<string name="pubkey_available">Öffentlicher Schlüssel verfügbar</string>
<string name="pubkey_not_available">Öffentlicher Schlüssel noch nicht verfügbar</string>
</resources>

View File

@ -59,4 +59,6 @@
<string name="status">Debugging</string>
<string name="status_summary">Technical information</string>
<string name="alias_default_identity">Me</string>
<string name="pubkey_available">Public key available</string>
<string name="pubkey_not_available">Public key not yet available</string>
</resources>