Bumped Jabit version to prepare for conversations

This commit is contained in:
Christian Basler 2017-03-18 07:09:03 +01:00
parent 22ac1920a2
commit 42cf18445c
10 changed files with 176 additions and 37 deletions

View File

@ -40,9 +40,9 @@ android {
ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT' ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT'
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.1.0' compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:support-v4:25.1.0' compile 'com.android.support:support-v4:25.3.0'
compile 'com.android.support:design:25.1.0' compile 'com.android.support:design:25.3.0'
compile "ch.dissem.jabit:jabit-core:$jabitVersion" compile "ch.dissem.jabit:jabit-core:$jabitVersion"
compile "ch.dissem.jabit:jabit-networking:$jabitVersion" compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
@ -52,25 +52,25 @@ dependencies {
compile 'org.slf4j:slf4j-android:1.7.12' compile 'org.slf4j:slf4j-android:1.7.12'
compile 'com.mikepenz:materialize:1.0.0@aar' compile 'com.mikepenz:materialize:1.0.1@aar'
compile('com.mikepenz:materialdrawer:5.6.0@aar') { compile('com.mikepenz:materialdrawer:5.8.2@aar') {
transitive = true transitive = true
} }
compile('com.mikepenz:aboutlibraries:5.8.1@aar') { compile('com.mikepenz:aboutlibraries:5.9.4@aar') {
transitive = true transitive = true
} }
compile 'com.mikepenz:iconics:1.6.2@aar' compile 'com.mikepenz:iconics:1.6.2@aar'
compile 'com.mikepenz:community-material-typeface:1.5.54.2@aar' compile 'com.mikepenz:community-material-typeface:1.8.36.1@aar'
compile 'com.journeyapps:zxing-android-embedded:3.3.0@aar' compile 'com.journeyapps:zxing-android-embedded:3.3.0@aar'
compile 'com.google.zxing:core:3.3.0' compile 'com.google.zxing:core:3.3.0'
compile 'io.github.yavski:fab-speed-dial:1.0.6' compile 'io.github.yavski:fab-speed-dial:1.0.6'
compile 'com.github.amlcurran.showcaseview:library:5.4.3' compile 'com.github.amlcurran.showcaseview:library:5.4.3'
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.9.3@aar') { compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.4@aar') {
transitive = true transitive = true
} }
compile 'com.github.angads25:filepicker:1.0.6' compile 'com.github.angads25:filepicker:1.0.9'
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4' compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'

View File

@ -0,0 +1,11 @@
ALTER TABLE Message ADD COLUMN conversation BINARY[16];
CREATE TABLE Message_Parent (
parent BINARY(64) NOT NULL,
child BINARY(64) NOT NULL,
pos INT NOT NULL,
conversation BINARY[16] NOT NULL,
PRIMARY KEY (parent, child),
FOREIGN KEY (child) REFERENCES Message (iv)
);

View File

@ -55,6 +55,7 @@ import java.io.Serializable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import ch.dissem.apps.abit.dialog.AddIdentityDialogFragment; import ch.dissem.apps.abit.dialog.AddIdentityDialogFragment;
import ch.dissem.apps.abit.dialog.FullNodeDialogActivity; import ch.dissem.apps.abit.dialog.FullNodeDialogActivity;
@ -311,7 +312,15 @@ public class MainActivity extends AppCompatActivity
public boolean onItemClick(View view, int position, IDrawerItem item) { public boolean onItemClick(View view, int position, IDrawerItem item) {
if (item.getTag() instanceof Label) { if (item.getTag() instanceof Label) {
selectedLabel = (Label) item.getTag(); selectedLabel = (Label) item.getTag();
showSelectedLabel(); if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
MessageListFragment) {
((MessageListFragment) getSupportFragmentManager()
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
} else {
MessageListFragment listFragment = new MessageListFragment();
changeList(listFragment);
listFragment.updateList(selectedLabel);
}
return false; return false;
} else if (item instanceof Nameable<?>) { } else if (item instanceof Nameable<?>) {
Nameable<?> ni = (Nameable<?>) item; Nameable<?> ni = (Nameable<?>) item;
@ -374,7 +383,7 @@ public class MainActivity extends AppCompatActivity
for (Label label : labels) { for (Label label : labels) {
addLabelEntry(label); addLabelEntry(label);
} }
showSelectedLabel(); drawer.setSelection(drawer.getDrawerItem(selectedLabel));
} }
}.execute(); }.execute();
} }
@ -389,7 +398,9 @@ public class MainActivity extends AppCompatActivity
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void onRestoreInstanceState(Bundle savedInstanceState) { protected void onRestoreInstanceState(Bundle savedInstanceState) {
selectedLabel = (Label) savedInstanceState.getSerializable("selectedLabel"); selectedLabel = (Label) savedInstanceState.getSerializable("selectedLabel");
showSelectedLabel();
drawer.setSelection(drawer.getDrawerItem(selectedLabel));
super.onRestoreInstanceState(savedInstanceState); super.onRestoreInstanceState(savedInstanceState);
} }
@ -439,7 +450,7 @@ public class MainActivity extends AppCompatActivity
item.withIcon(CommunityMaterial.Icon.cmd_file); item.withIcon(CommunityMaterial.Icon.cmd_file);
break; break;
case OUTBOX: case OUTBOX:
item.withIcon(CommunityMaterial.Icon.cmd_outbox); item.withIcon(CommunityMaterial.Icon.cmd_inbox_arrow_up);
break; break;
case SENT: case SENT:
item.withIcon(CommunityMaterial.Icon.cmd_send); item.withIcon(CommunityMaterial.Icon.cmd_send);
@ -521,18 +532,6 @@ public class MainActivity extends AppCompatActivity
} }
} }
private void showSelectedLabel() {
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
MessageListFragment) {
((MessageListFragment) getSupportFragmentManager()
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
} else {
MessageListFragment listFragment = new MessageListFragment();
changeList(listFragment);
listFragment.updateList(selectedLabel);
}
}
/** /**
* Callback method from {@link ListSelectionListener} * Callback method from {@link ListSelectionListener}
* indicating that the item with the given ID was selected. * indicating that the item with the given ID was selected.

View File

@ -29,8 +29,10 @@ import android.widget.Toast;
import com.mikepenz.aboutlibraries.Libs; import com.mikepenz.aboutlibraries.Libs;
import com.mikepenz.aboutlibraries.LibsBuilder; import com.mikepenz.aboutlibraries.LibsBuilder;
import ch.dissem.apps.abit.repository.AndroidNodeRegistry;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.apps.abit.synchronization.SyncAdapter; import ch.dissem.apps.abit.synchronization.SyncAdapter;
import ch.dissem.bitmessage.BitmessageContext;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW; import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE; import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
@ -78,7 +80,9 @@ public class SettingsFragment
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
Singleton.getBitmessageContext(ctx).cleanup(); BitmessageContext bmc = Singleton.getBitmessageContext(ctx);
bmc.cleanup();
bmc.internals().getNodeRegistry().clear();
return null; return null;
} }

View File

@ -31,8 +31,10 @@ import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID;
import ch.dissem.apps.abit.R; import ch.dissem.apps.abit.R;
import ch.dissem.apps.abit.util.UuidUtils;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
@ -40,6 +42,8 @@ import ch.dissem.bitmessage.ports.AbstractMessageRepository;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import static ch.dissem.apps.abit.util.UuidUtils.asUuid;
import static ch.dissem.bitmessage.utils.Strings.hex;
import static java.lang.String.valueOf; import static java.lang.String.valueOf;
/** /**
@ -65,6 +69,9 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
private static final String COLUMN_RETRIES = "retries"; private static final String COLUMN_RETRIES = "retries";
private static final String COLUMN_NEXT_TRY = "next_try"; private static final String COLUMN_NEXT_TRY = "next_try";
private static final String COLUMN_INITIAL_HASH = "initial_hash"; private static final String COLUMN_INITIAL_HASH = "initial_hash";
private static final String COLUMN_CONVERSATION = "conversation";
private static final String PARENTS_TABLE_NAME = "Message_Parent";
private static final String JOIN_TABLE_NAME = "Message_Label"; private static final String JOIN_TABLE_NAME = "Message_Label";
private static final String JT_COLUMN_MESSAGE = "message_id"; private static final String JT_COLUMN_MESSAGE = "message_id";
@ -164,7 +171,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
public int countUnread(Label label) { public int countUnread(Label label) {
String[] args; String[] args;
String where; String where;
if (label == null){ if (label == null) {
return 0; return 0;
} }
if (label == LABEL_ARCHIVE) { if (label == LABEL_ARCHIVE) {
@ -187,6 +194,72 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
); );
} }
@Override
public List<UUID> findConversations(Label label) {
String[] projection = {
COLUMN_CONVERSATION,
};
String where;
if (label == null) {
where = "id NOT IN (SELECT message_id FROM Message_Label)";
} else {
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")";
}
List<UUID> result = new LinkedList<>();
SQLiteDatabase db = sql.getReadableDatabase();
try (Cursor c = db.query(
TABLE_NAME, projection,
where,
null, null, null, null
)) {
while (c.moveToNext()) {
byte[] uuidBytes = c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION));
result.add(asUuid(uuidBytes));
}
}
return result;
}
private void updateParents(SQLiteDatabase db, Plaintext message) {
if (message.getInventoryVector() == null || message.getParents().isEmpty()) {
// There are no parents to save yet (they are saved in the extended data, that's enough for now)
return;
}
db.delete(PARENTS_TABLE_NAME, "child=?", new String[]{hex(message.getInitialHash()).toString()});
byte[] childIV = message.getInventoryVector().getHash();
// 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);
}
}
/**
* Replaces every occurrence of the source conversation ID with the target ID
*
* @param db is used to keep everything within one transaction
* @param source ID of the conversation to be merged
* @param target ID of the merge target
*/
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()};
db.update(TABLE_NAME, values, "conversation=?", whereArgs);
db.update(PARENTS_TABLE_NAME, values, "conversation=?", whereArgs);
}
protected List<Plaintext> find(String where) { protected List<Plaintext> find(String where) {
List<Plaintext> result = new LinkedList<>(); List<Plaintext> result = new LinkedList<>();
@ -205,7 +278,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
COLUMN_STATUS, COLUMN_STATUS,
COLUMN_TTL, COLUMN_TTL,
COLUMN_RETRIES, COLUMN_RETRIES,
COLUMN_NEXT_TRY COLUMN_NEXT_TRY,
COLUMN_CONVERSATION
}; };
SQLiteDatabase db = sql.getReadableDatabase(); SQLiteDatabase db = sql.getReadableDatabase();
@ -220,8 +294,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA)); byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex
(COLUMN_TYPE))); (COLUMN_TYPE)));
Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new Plaintext.Builder builder = Plaintext.readWithoutSignature(type,
ByteArrayInputStream(data)); new ByteArrayInputStream(data));
long id = c.getLong(c.getColumnIndex(COLUMN_ID)); long id = c.getLong(c.getColumnIndex(COLUMN_ID));
builder.id(id); builder.id(id);
builder.IV(new InventoryVector(iv)); builder.IV(new InventoryVector(iv));
@ -240,6 +314,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
if (!c.isNull(nextTryColumn)) { if (!c.isNull(nextTryColumn)) {
builder.nextTry(c.getLong(nextTryColumn)); builder.nextTry(c.getLong(nextTryColumn));
} }
builder.conversation(asUuid(c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION))));
builder.labels(findLabels(id)); builder.labels(findLabels(id));
result.add(builder.build()); result.add(builder.build());
} }
@ -268,6 +343,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
update(db, message); update(db, message);
} }
updateParents(db, message);
// remove existing labels // remove existing labels
db.delete(JOIN_TABLE_NAME, "message_id=?", new String[]{valueOf(message.getId())}); db.delete(JOIN_TABLE_NAME, "message_id=?", new String[]{valueOf(message.getId())});
@ -302,6 +379,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
values.put(COLUMN_TTL, message.getTTL()); values.put(COLUMN_TTL, message.getTTL());
values.put(COLUMN_RETRIES, message.getRetries()); values.put(COLUMN_RETRIES, message.getRetries());
values.put(COLUMN_NEXT_TRY, message.getNextTry()); values.put(COLUMN_NEXT_TRY, message.getNextTry());
values.put(COLUMN_CONVERSATION, UuidUtils.asBytes(message.getConversationId()));
long id = db.insertOrThrow(TABLE_NAME, null, values); long id = db.insertOrThrow(TABLE_NAME, null, values);
message.setId(id); message.setId(id);
} }
@ -322,6 +400,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
values.put(COLUMN_TTL, message.getTTL()); values.put(COLUMN_TTL, message.getTTL());
values.put(COLUMN_RETRIES, message.getRetries()); values.put(COLUMN_RETRIES, message.getRetries());
values.put(COLUMN_NEXT_TRY, message.getNextTry()); values.put(COLUMN_NEXT_TRY, message.getNextTry());
values.put(COLUMN_CONVERSATION, UuidUtils.asBytes(message.getConversationId()));
db.update(TABLE_NAME, values, "id = " + message.getId(), null); db.update(TABLE_NAME, values, "id = " + message.getId(), null);
} }

View File

@ -10,6 +10,7 @@ import android.database.sqlite.SQLiteStatement;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -56,6 +57,12 @@ public class AndroidNodeRegistry implements NodeRegistry {
db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))}); db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))});
} }
@Override
public void clear() {
SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, null, null);
}
private Long loadExistingTime(NetworkAddress node) { private Long loadExistingTime(NetworkAddress node) {
SQLiteStatement statement = loadExistingStatement.get(); SQLiteStatement statement = loadExistingStatement.get();
if (statement == null) { if (statement == null) {

View File

@ -27,7 +27,7 @@ import ch.dissem.apps.abit.util.Assets;
*/ */
public class SqlHelper extends SQLiteOpenHelper { public class SqlHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version. // If you change the database schema, you must increment the database version.
private static final int DATABASE_VERSION = 6; private static final int DATABASE_VERSION = 7;
private static final String DATABASE_NAME = "jabit.db"; private static final String DATABASE_NAME = "jabit.db";
private final Context ctx; private final Context ctx;
@ -61,6 +61,8 @@ public class SqlHelper extends SQLiteOpenHelper {
executeMigration(db, "V3.3__Create_table_node"); executeMigration(db, "V3.3__Create_table_node");
case 5: case 5:
executeMigration(db, "V3.4__Add_label_outbox"); executeMigration(db, "V3.4__Add_label_outbox");
case 6:
executeMigration(db, "V4.0__Create_table_message_parent");
default: default:
// Nothing to do. Let's assume we won't upgrade from a version that's newer than // Nothing to do. Let's assume we won't upgrade from a version that's newer than
// DATABASE_VERSION. // DATABASE_VERSION.
@ -73,7 +75,7 @@ public class SqlHelper extends SQLiteOpenHelper {
} }
} }
public static StringBuilder join(long... numbers) { static StringBuilder join(long... numbers) {
StringBuilder streamList = new StringBuilder(); StringBuilder streamList = new StringBuilder();
for (int i = 0; i < numbers.length; i++) { for (int i = 0; i < numbers.length; i++) {
if (i > 0) streamList.append(", "); if (i > 0) streamList.append(", ");
@ -82,7 +84,7 @@ public class SqlHelper extends SQLiteOpenHelper {
return streamList; return streamList;
} }
public static StringBuilder join(Enum<?>... types) { static StringBuilder join(Enum<?>... types) {
StringBuilder streamList = new StringBuilder(); StringBuilder streamList = new StringBuilder();
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
if (i > 0) streamList.append(", "); if (i > 0) streamList.append(", ");

View File

@ -0,0 +1,37 @@
package ch.dissem.apps.abit.util;
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* SQLite has no UUID data type, and UUIDs are therefore best saved as BINARY[16]. This class
* takes care of conversion between byte[16] and UUID.
* <p>
* Thanks to Brice Roncace on
* <a href="http://stackoverflow.com/questions/17893609/convert-uuid-to-byte-that-works-when-using-uuid-nameuuidfrombytesb">
* Stack Overflow
* </a>
* for providing the UUID <-> byte[] conversions.
* </p>
*/
public class UuidUtils {
public static UUID asUuid(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
long firstLong = bb.getLong();
long secondLong = bb.getLong();
return new UUID(firstLong, secondLong);
}
public static byte[] asBytes(UUID uuid) {
if (uuid == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
}

View File

@ -9,7 +9,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -1,6 +1,6 @@
#Fri Aug 12 22:10:25 CEST 2016 #Thu Mar 09 06:38:41 CET 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip