Minor bug fixes and improvements, added caching to inventory
This commit is contained in:
parent
6ec25cfae7
commit
6f6a2e4e6c
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
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);
|
||||
|
||||
String[] projection = {
|
||||
COLUMN_HASH
|
||||
COLUMN_HASH, COLUMN_EXPIRES
|
||||
};
|
||||
|
||||
SQLiteDatabase db = sql.getReadableDatabase();
|
||||
Cursor c = db.query(
|
||||
try (Cursor c = db.query(
|
||||
TABLE_NAME, projection,
|
||||
(includeExpired ? "" : "expires > " + now() + " AND ") + "stream IN (" + join
|
||||
(streams) + ")",
|
||||
"stream = " + stream,
|
||||
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));
|
||||
long expires = c.getLong(c.getColumnIndex(COLUMN_EXPIRES));
|
||||
result.put(new InventoryVector(blob), expires);
|
||||
c.moveToNext();
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
LOG.info("Inventory size: " + result.size());
|
||||
LOG.info("Stream #" + stream + " 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
app/src/main/res/drawable/public_key.xml
Normal file
8
app/src/main/res/drawable/public_key.xml
Normal 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>
|
@ -16,6 +16,7 @@
|
||||
-->
|
||||
|
||||
<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">
|
||||
|
||||
@ -23,58 +24,82 @@
|
||||
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:layout_alignParentTop="true"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@color/colorAccent"
|
||||
android:layout_margin="16dp"/>
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Name"
|
||||
android:layout_alignParentEnd="true"
|
||||
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: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: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:layout_below="@+id/address"
|
||||
android:ellipsize="end"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:lines="1"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:layout_below="@+id/address"/>
|
||||
android:text="Stream #"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
tools:ignore="HardcodedText"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/active"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/subscribed"
|
||||
android:id="@+id/active"
|
||||
android:paddingTop="16dp"
|
||||
android:layout_below="@+id/stream_number"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:layout_below="@+id/stream_number"/>
|
||||
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>
|
@ -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>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user