Split LabelRepository off the MessageRepository

This commit is contained in:
Christian Basler 2017-11-27 22:06:25 +01:00
parent f45f6c3919
commit 760e423b9b
11 changed files with 356 additions and 279 deletions

View File

@ -28,7 +28,7 @@ import android.widget.RelativeLayout
import ch.dissem.apps.abit.drawer.ProfileImageListener import ch.dissem.apps.abit.drawer.ProfileImageListener
import ch.dissem.apps.abit.drawer.ProfileSelectionListener import ch.dissem.apps.abit.drawer.ProfileSelectionListener
import ch.dissem.apps.abit.listener.ListSelectionListener import ch.dissem.apps.abit.listener.ListSelectionListener
import ch.dissem.apps.abit.repository.AndroidMessageRepository.Companion.LABEL_ARCHIVE import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
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.apps.abit.util.Labels import ch.dissem.apps.abit.util.Labels
@ -278,7 +278,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
} }
doAsync { doAsync {
val labels = bmc.messages.getLabels() val labels = bmc.labels.getLabels()
uiThread { uiThread {
if (intent.hasExtra(EXTRA_SHOW_LABEL)) { if (intent.hasExtra(EXTRA_SHOW_LABEL)) {

View File

@ -28,7 +28,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import ch.dissem.apps.abit.Identicon import ch.dissem.apps.abit.Identicon
import ch.dissem.apps.abit.R import ch.dissem.apps.abit.R
import ch.dissem.apps.abit.repository.AndroidMessageRepository.Companion.LABEL_ARCHIVE import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
import ch.dissem.apps.abit.util.Assets import ch.dissem.apps.abit.util.Assets
import ch.dissem.apps.abit.util.Strings.prepareMessageExtract import ch.dissem.apps.abit.util.Strings.prepareMessageExtract
import ch.dissem.bitmessage.entity.Plaintext import ch.dissem.bitmessage.entity.Plaintext

View File

@ -0,0 +1,113 @@
/*
* Copyright 2015 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.repository
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.DatabaseUtils
import ch.dissem.apps.abit.util.Labels
import ch.dissem.bitmessage.entity.valueobject.Label
import ch.dissem.bitmessage.ports.AbstractLabelRepository
import ch.dissem.bitmessage.ports.MessageRepository
import java.util.*
/**
* [MessageRepository] implementation using the Android SQL API.
*/
class AndroidLabelRepository(private val sql: SqlHelper, private val context: Context) : AbstractLabelRepository() {
override fun find(where: String): List<Label> {
val result = LinkedList<Label>()
// Define a projection that specifies which columns from the database
// you will actually use after this query.
val projection = arrayOf(COLUMN_ID, COLUMN_LABEL, COLUMN_TYPE, COLUMN_COLOR)
sql.readableDatabase.query(
TABLE_NAME, projection,
where, null, null, null,
COLUMN_ORDER
).use { c ->
while (c.moveToNext()) {
result.add(getLabel(c, context))
}
}
return result
}
override fun save(label: Label) {
val db = sql.writableDatabase
if (label.id != null) {
val values = ContentValues()
values.put(COLUMN_LABEL, label.toString())
values.put(COLUMN_TYPE, label.type?.name)
values.put(COLUMN_COLOR, label.color)
values.put(COLUMN_ORDER, label.ord)
db.update(TABLE_NAME, values, "id=?", arrayOf(label.id.toString()))
} else {
try {
db.beginTransaction()
val exists = DatabaseUtils.queryNumEntries(db, TABLE_NAME, "label=?", arrayOf(label.toString())) > 0
if (exists) {
val values = ContentValues()
values.put(COLUMN_TYPE, label.type?.name)
values.put(COLUMN_COLOR, label.color)
values.put(COLUMN_ORDER, label.ord)
db.update(TABLE_NAME, values, "label=?", arrayOf(label.toString()))
} else {
val values = ContentValues()
values.put(COLUMN_LABEL, label.toString())
values.put(COLUMN_TYPE, label.type?.name)
values.put(COLUMN_COLOR, label.color)
values.put(COLUMN_ORDER, label.ord)
db.insertOrThrow(TABLE_NAME, null, values)
}
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}
internal fun findLabels(msgId: Any) = find("id IN (SELECT label_id FROM Message_Label WHERE message_id=$msgId)")
companion object {
val LABEL_ARCHIVE = Label("archive", null, 0)
private const val TABLE_NAME = "Label"
private const val COLUMN_ID = "id"
private const val COLUMN_LABEL = "label"
private const val COLUMN_TYPE = "type"
private const val COLUMN_COLOR = "color"
private const val COLUMN_ORDER = "ord"
internal fun getLabel(c: Cursor, context: Context): Label {
val typeName = c.getString(c.getColumnIndex(COLUMN_TYPE))
val type = if (typeName == null) null else Label.Type.valueOf(typeName)
val text: String? = Labels.getText(type, null, context)
val label = Label(
text ?: c.getString(c.getColumnIndex(COLUMN_LABEL)),
type,
c.getInt(c.getColumnIndex(COLUMN_COLOR)))
label.id = c.getLong(c.getColumnIndex(COLUMN_ID))
return label
}
}
}

View File

@ -17,12 +17,11 @@
package ch.dissem.apps.abit.repository package ch.dissem.apps.abit.repository
import android.content.ContentValues import android.content.ContentValues
import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.database.DatabaseUtils import android.database.DatabaseUtils
import android.database.sqlite.SQLiteConstraintException import android.database.sqlite.SQLiteConstraintException
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import ch.dissem.apps.abit.util.Labels import ch.dissem.apps.abit.repository.AndroidLabelRepository.Companion.LABEL_ARCHIVE
import ch.dissem.apps.abit.util.UuidUtils import ch.dissem.apps.abit.util.UuidUtils
import ch.dissem.apps.abit.util.UuidUtils.asUuid import ch.dissem.apps.abit.util.UuidUtils.asUuid
import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.BitmessageAddress
@ -40,7 +39,7 @@ import java.util.*
/** /**
* [MessageRepository] implementation using the Android SQL API. * [MessageRepository] implementation using the Android SQL API.
*/ */
class AndroidMessageRepository(private val sql: SqlHelper, private val context: Context) : AbstractMessageRepository() { class AndroidMessageRepository(private val sql: SqlHelper) : AbstractMessageRepository() {
override fun findMessages(label: Label?, offset: Int, limit: Int) = if (label === LABEL_ARCHIVE) { override fun findMessages(label: Label?, offset: Int, limit: Int) = if (label === LABEL_ARCHIVE) {
super.findMessages(null as Label?, offset, limit) super.findMessages(null as Label?, offset, limit)
@ -48,87 +47,20 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
super.findMessages(label, offset, limit) super.findMessages(label, offset, limit)
} }
public override fun findLabels(where: String): List<Label> {
val result = LinkedList<Label>()
// Define a projection that specifies which columns from the database
// you will actually use after this query.
val projection = arrayOf(LBL_COLUMN_ID, LBL_COLUMN_LABEL, LBL_COLUMN_TYPE, LBL_COLUMN_COLOR)
sql.readableDatabase.query(
LBL_TABLE_NAME, projection,
where, null, null, null,
LBL_COLUMN_ORDER
).use { c ->
while (c.moveToNext()) {
result.add(getLabel(c))
}
}
return result
}
private fun getLabel(c: Cursor): Label {
val typeName = c.getString(c.getColumnIndex(LBL_COLUMN_TYPE))
val type = if (typeName == null) null else Label.Type.valueOf(typeName)
val text: String? = Labels.getText(type, null, context)
val label = Label(
text ?: c.getString(c.getColumnIndex(LBL_COLUMN_LABEL)),
type,
c.getInt(c.getColumnIndex(LBL_COLUMN_COLOR)))
label.id = c.getLong(c.getColumnIndex(LBL_COLUMN_ID))
return label
}
override fun save(label: Label) {
val db = sql.writableDatabase
if (label.id != null) {
val values = ContentValues()
values.put(LBL_COLUMN_LABEL, label.toString())
values.put(LBL_COLUMN_TYPE, label.type?.name)
values.put(LBL_COLUMN_COLOR, label.color)
values.put(LBL_COLUMN_ORDER, label.ord)
db.update(LBL_TABLE_NAME, values, "id=?", arrayOf(label.id.toString()))
} else {
try {
db.beginTransaction()
val exists = DatabaseUtils.queryNumEntries(db, LBL_TABLE_NAME, "label=?", arrayOf(label.toString())) > 0
if (exists) {
val values = ContentValues()
values.put(LBL_COLUMN_TYPE, label.type?.name)
values.put(LBL_COLUMN_COLOR, label.color)
values.put(LBL_COLUMN_ORDER, label.ord)
db.update(LBL_TABLE_NAME, values, "label=?", arrayOf(label.toString()))
} else {
val values = ContentValues()
values.put(LBL_COLUMN_LABEL, label.toString())
values.put(LBL_COLUMN_TYPE, label.type?.name)
values.put(LBL_COLUMN_COLOR, label.color)
values.put(LBL_COLUMN_ORDER, label.ord)
db.insertOrThrow(LBL_TABLE_NAME, null, values)
}
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}
override fun countUnread(label: Label?) = when { override fun countUnread(label: Label?) = when {
label === LABEL_ARCHIVE -> 0 label === LABEL_ARCHIVE -> 0
label == null -> DatabaseUtils.queryNumEntries( label == null -> DatabaseUtils.queryNumEntries(
sql.readableDatabase, sql.readableDatabase,
TABLE_NAME, TABLE_NAME,
"id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))", "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))",
arrayOf(Label.Type.UNREAD.name) arrayOf(Label.Type.UNREAD.name)
).toInt() ).toInt()
else -> DatabaseUtils.queryNumEntries( else -> DatabaseUtils.queryNumEntries(
sql.readableDatabase, sql.readableDatabase,
TABLE_NAME, TABLE_NAME,
" id IN (SELECT message_id FROM Message_Label WHERE label_id=?) " + " id IN (SELECT message_id FROM Message_Label WHERE label_id=?) " +
"AND id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))", "AND id IN (SELECT message_id FROM Message_Label WHERE label_id IN (SELECT id FROM Label WHERE type=?))",
arrayOf(label.id.toString(), Label.Type.UNREAD.name) arrayOf(label.id.toString(), Label.Type.UNREAD.name)
).toInt() ).toInt()
} }
@ -142,9 +74,9 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
} }
val result = LinkedList<UUID>() val result = LinkedList<UUID>()
sql.readableDatabase.query( sql.readableDatabase.query(
true, true,
TABLE_NAME, projection, where, TABLE_NAME, projection, where,
null, null, null, null, null null, null, null, null, null
).use { c -> ).use { c ->
while (c.moveToNext()) { while (c.moveToNext()) {
val uuidBytes = c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION)) val uuidBytes = c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION))
@ -166,11 +98,11 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
// save new parents // save new parents
var order = 0 var order = 0
val values = ContentValues()
for (parentIV in message.parents) { for (parentIV in message.parents) {
getMessage(parentIV)?.let { parent -> getMessage(parentIV)?.let { parent ->
mergeConversations(db, parent.conversationId, message.conversationId) mergeConversations(db, parent.conversationId, message.conversationId)
order++ order++
val values = ContentValues()
values.put("parent", parentIV.hash) values.put("parent", parentIV.hash)
values.put("child", childIV) values.put("child", childIV)
values.put("pos", order) values.put("pos", order)
@ -206,57 +138,45 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
val db = sql.readableDatabase val db = sql.readableDatabase
db.query( db.query(
TABLE_NAME, projection, TABLE_NAME, projection,
where, null, null, null, where, null, null, null,
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC", "$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
if (limit == 0) null else "$offset, $limit" if (limit == 0) null else "$offset, $limit"
).use { c -> ).use { c ->
while (c.moveToNext()) { while (c.moveToNext()) {
val iv = c.getBlob(c.getColumnIndex(COLUMN_IV)) result.add(getMessage(c))
val data = c.getBlob(c.getColumnIndex(COLUMN_DATA))
val type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex(COLUMN_TYPE)))
val builder = Plaintext.readWithoutSignature(type,
ByteArrayInputStream(data))
val id = c.getLong(c.getColumnIndex(COLUMN_ID))
builder.id(id)
builder.IV(InventoryVector.fromHash(iv))
val sender = c.getString(c.getColumnIndex(COLUMN_SENDER))
if (sender != null) {
val address = ctx.addressRepository.getAddress(sender)
if (address != null) {
builder.from(address)
} else {
builder.from(BitmessageAddress(sender))
}
}
val recipient = c.getString(c.getColumnIndex(COLUMN_RECIPIENT))
if (recipient != null) {
val address = ctx.addressRepository.getAddress(recipient)
if (address != null) {
builder.to(address)
} else {
builder.to(BitmessageAddress(sender))
}
}
builder.ackData(c.getBlob(c.getColumnIndex(COLUMN_ACK_DATA)))
builder.sent(c.getLong(c.getColumnIndex(COLUMN_SENT)))
builder.received(c.getLong(c.getColumnIndex(COLUMN_RECEIVED)))
builder.status(Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS))))
builder.ttl(c.getLong(c.getColumnIndex(COLUMN_TTL)))
builder.retries(c.getInt(c.getColumnIndex(COLUMN_RETRIES)))
val nextTryColumn = c.getColumnIndex(COLUMN_NEXT_TRY)
if (!c.isNull(nextTryColumn)) {
builder.nextTry(c.getLong(nextTryColumn))
}
builder.conversation(asUuid(c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION))))
builder.labels(findLabels(id))
result.add(builder.build())
} }
} }
return result return result
} }
private fun findLabels(id: Long) = findLabels("id IN (SELECT label_id FROM Message_Label WHERE message_id=$id)") private fun getMessage(c: Cursor): Plaintext = Plaintext.readWithoutSignature(
Plaintext.Type.valueOf(c.getString(c.getColumnIndex(COLUMN_TYPE))),
ByteArrayInputStream(c.getBlob(c.getColumnIndex(COLUMN_DATA)))
).build {
id = c.getLong(c.getColumnIndex(COLUMN_ID))
inventoryVector = InventoryVector.fromHash(c.getBlob(c.getColumnIndex(COLUMN_IV)))
c.getString(c.getColumnIndex(COLUMN_SENDER))?.let {
from = ctx.addressRepository.getAddress(it) ?: BitmessageAddress(it)
}
c.getString(c.getColumnIndex(COLUMN_RECIPIENT))?.let {
to = ctx.addressRepository.getAddress(it) ?: BitmessageAddress(it)
}
ackData = c.getBlob(c.getColumnIndex(COLUMN_ACK_DATA))
sent = c.getLong(c.getColumnIndex(COLUMN_SENT))
received = c.getLong(c.getColumnIndex(COLUMN_RECEIVED))
status = Plaintext.Status.valueOf(c.getString(c.getColumnIndex(COLUMN_STATUS)))
ttl = c.getLong(c.getColumnIndex(COLUMN_TTL))
retries = c.getInt(c.getColumnIndex(COLUMN_RETRIES))
val nextTryColumn = c.getColumnIndex(COLUMN_NEXT_TRY)
if (!c.isNull(nextTryColumn)) {
nextTry = c.getLong(nextTryColumn)
}
conversation = asUuid(c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION)))
labels = findLabels(id!!)
}
private fun findLabels(msgId: Any) = (ctx.labelRepository as AndroidLabelRepository).findLabels(msgId)
override fun save(message: Plaintext) { override fun save(message: Plaintext) {
saveContactIfNecessary(message.from) saveContactIfNecessary(message.from)
@ -326,8 +246,6 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
} }
companion object { companion object {
val LABEL_ARCHIVE = Label("archive", null, 0)
private const val TABLE_NAME = "Message" private const val TABLE_NAME = "Message"
private const val COLUMN_ID = "id" private const val COLUMN_ID = "id"
private const val COLUMN_IV = "iv" private const val COLUMN_IV = "iv"
@ -350,12 +268,5 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
private const val JOIN_TABLE_NAME = "Message_Label" private const val JOIN_TABLE_NAME = "Message_Label"
private const val JT_COLUMN_MESSAGE = "message_id" private const val JT_COLUMN_MESSAGE = "message_id"
private const val JT_COLUMN_LABEL = "label_id" private const val JT_COLUMN_LABEL = "label_id"
private const val LBL_TABLE_NAME = "Label"
private const val LBL_COLUMN_ID = "id"
private const val LBL_COLUMN_LABEL = "label"
private const val LBL_COLUMN_TYPE = "type"
private const val LBL_COLUMN_COLOR = "color"
private const val LBL_COLUMN_ORDER = "ord"
} }
} }

View File

@ -52,32 +52,33 @@ object Singleton {
fun getBitmessageContext(context: Context): BitmessageContext = fun getBitmessageContext(context: Context): BitmessageContext =
init({ bitmessageContext }, { bitmessageContext = it }) { init({ bitmessageContext }, { bitmessageContext = it }) {
val ctx = context.applicationContext BitmessageContext.build {
val sqlHelper = SqlHelper(ctx) TTL.pubkey = 2 * DAY
val powRepo = AndroidProofOfWorkRepository(sqlHelper) val ctx = context.applicationContext
this.powRepo = powRepo val sqlHelper = SqlHelper(ctx)
TTL.pubkey = 2 * DAY proofOfWorkEngine = SwitchingProofOfWorkEngine(
BitmessageContext.Builder() ctx, Constants.PREFERENCE_SERVER_POW,
.proofOfWorkEngine(SwitchingProofOfWorkEngine( ServerPowEngine(ctx),
ctx, Constants.PREFERENCE_SERVER_POW, ServicePowEngine(ctx)
ServerPowEngine(ctx), )
ServicePowEngine(ctx) cryptography = AndroidCryptography()
)) nodeRegistry = AndroidNodeRegistry(sqlHelper)
.cryptography(AndroidCryptography()) inventory = AndroidInventory(sqlHelper)
.nodeRegistry(AndroidNodeRegistry(sqlHelper)) addressRepo = AndroidAddressRepository(sqlHelper)
.inventory(AndroidInventory(sqlHelper)) labelRepo = AndroidLabelRepository(sqlHelper, ctx)
.addressRepo(AndroidAddressRepository(sqlHelper)) messageRepo = AndroidMessageRepository(sqlHelper)
.messageRepo(AndroidMessageRepository(sqlHelper, ctx)) proofOfWorkRepo = AndroidProofOfWorkRepository(sqlHelper).also { powRepo = it }
.powRepo(powRepo) networkHandler = NioNetworkHandler()
.networkHandler(NioNetworkHandler()) listener = getMessageListener(ctx)
.listener(getMessageListener(ctx)) labeler = Singleton.labeler
.labeler(labeler) preferences.sendPubkeyOnIdentityCreation = false
.doNotSendPubkeyOnIdentityCreation() }
.build()
} }
fun getMessageListener(ctx: Context) = init({ messageListener }, { messageListener = it }) { MessageListener(ctx) } fun getMessageListener(ctx: Context) = init({ messageListener }, { messageListener = it }) { MessageListener(ctx) }
fun getLabelRepository(ctx: Context) = getBitmessageContext(ctx).labels as AndroidLabelRepository
fun getMessageRepository(ctx: Context) = getBitmessageContext(ctx).messages as AndroidMessageRepository fun getMessageRepository(ctx: Context) = getBitmessageContext(ctx).messages as AndroidMessageRepository
fun getAddressRepository(ctx: Context) = getBitmessageContext(ctx).addresses as AndroidAddressRepository fun getAddressRepository(ctx: Context) = getBitmessageContext(ctx).addresses as AndroidAddressRepository
@ -94,15 +95,15 @@ object Singleton {
creatingIdentity = true creatingIdentity = true
doAsync { doAsync {
val identity = bmc.createIdentity(false, val identity = bmc.createIdentity(false,
Pubkey.Feature.DOES_ACK) Pubkey.Feature.DOES_ACK)
identity.alias = ctx.getString(R.string.alias_default_identity) identity.alias = ctx.getString(R.string.alias_default_identity)
bmc.addresses.save(identity) bmc.addresses.save(identity)
uiThread { uiThread {
Singleton.identity = identity Singleton.identity = identity
Toast.makeText(ctx, Toast.makeText(ctx,
R.string.toast_identity_created, R.string.toast_identity_created,
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
val mainActivity = MainActivity.getInstance() val mainActivity = MainActivity.getInstance()
mainActivity?.addIdentityEntry(identity) mainActivity?.addIdentityEntry(identity)
} }

View File

@ -35,14 +35,15 @@ object Exports {
) )
zip.closeEntry() zip.closeEntry()
val messageRepo = Singleton.getMessageRepository(ctx) val labelRepo = Singleton.getLabelRepository(ctx)
zip.putNextEntry(ZipEntry("labels.json")) zip.putNextEntry(ZipEntry("labels.json"))
val exportLabels = MessageExport.exportLabels(messageRepo.getLabels()) val exportLabels = MessageExport.exportLabels(labelRepo.getLabels())
zip.write( zip.write(
exportLabels.toJsonString(true).toByteArray() exportLabels.toJsonString(true).toByteArray()
) )
zip.closeEntry() zip.closeEntry()
zip.putNextEntry(ZipEntry("messages.json")) zip.putNextEntry(ZipEntry("messages.json"))
val messageRepo = Singleton.getMessageRepository(ctx)
val exportMessages = MessageExport.exportMessages(messageRepo.getAllMessages()) val exportMessages = MessageExport.exportMessages(messageRepo.getAllMessages())
zip.write( zip.write(
exportMessages.toJsonString(true).toByteArray() exportMessages.toJsonString(true).toByteArray()
@ -62,13 +63,13 @@ object Exports {
bmc.addresses.save(contact) bmc.addresses.save(contact)
} }
} }
bmc.messages.getLabels().forEach { label -> bmc.labels.getLabels().forEach { label ->
labels[label.toString()] = label labels[label.toString()] = label
} }
processEntry(ctx, zipFile, "labels.json") { json -> processEntry(ctx, zipFile, "labels.json") { json ->
MessageExport.importLabels(json).forEach { label -> MessageExport.importLabels(json).forEach { label ->
if (!labels.contains(label.toString())) { if (!labels.contains(label.toString())) {
bmc.messages.save(label) bmc.labels.save(label)
labels[label.toString()] = label labels[label.toString()] = label
} }
} }

View File

@ -0,0 +1,60 @@
/*
* Copyright 2015 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.bitmessage.repository
import android.os.Build
import ch.dissem.apps.abit.repository.AndroidLabelRepository
import ch.dissem.apps.abit.repository.SqlHelper
import ch.dissem.bitmessage.entity.valueobject.Label
import ch.dissem.bitmessage.ports.LabelRepository
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
class AndroidLabelRepositoryTest : TestBase() {
private lateinit var repo: LabelRepository
@Before
fun setUp() {
RuntimeEnvironment.application.deleteDatabase(SqlHelper.DATABASE_NAME)
val sqlHelper = SqlHelper(RuntimeEnvironment.application)
repo = AndroidLabelRepository(sqlHelper, RuntimeEnvironment.application)
}
@Test
fun `ensure labels are retrieved`() {
val labels = repo.getLabels()
assertEquals(7, labels.size.toLong())
}
@Test
fun `ensure labels can be retrieved by type`() {
val labels = repo.getLabels(Label.Type.INBOX)
assertEquals(1, labels.size.toLong())
assertEquals("Inbox", labels[0].toString())
}
}

View File

@ -17,8 +17,8 @@
package ch.dissem.bitmessage.repository package ch.dissem.bitmessage.repository
import android.os.Build import android.os.Build
import ch.dissem.apps.abit.BuildConfig
import ch.dissem.apps.abit.repository.AndroidAddressRepository import ch.dissem.apps.abit.repository.AndroidAddressRepository
import ch.dissem.apps.abit.repository.AndroidLabelRepository
import ch.dissem.apps.abit.repository.AndroidMessageRepository import ch.dissem.apps.abit.repository.AndroidMessageRepository
import ch.dissem.apps.abit.repository.SqlHelper import ch.dissem.apps.abit.repository.SqlHelper
import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography
@ -31,12 +31,12 @@ import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding
import ch.dissem.bitmessage.entity.valueobject.Label import ch.dissem.bitmessage.entity.valueobject.Label
import ch.dissem.bitmessage.entity.valueobject.PrivateKey import ch.dissem.bitmessage.entity.valueobject.PrivateKey
import ch.dissem.bitmessage.entity.valueobject.extended.Message import ch.dissem.bitmessage.entity.valueobject.extended.Message
import ch.dissem.bitmessage.ports.LabelRepository
import ch.dissem.bitmessage.ports.MessageRepository import ch.dissem.bitmessage.ports.MessageRepository
import ch.dissem.bitmessage.utils.UnixTime import ch.dissem.bitmessage.utils.UnixTime
import org.hamcrest.BaseMatcher import org.hamcrest.BaseMatcher
import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.Description import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers.* import org.hamcrest.Matchers.*
import org.junit.Assert.* import org.junit.Assert.*
import org.junit.Before import org.junit.Before
@ -67,14 +67,16 @@ class AndroidMessageRepositoryTest : TestBase() {
val sqlHelper = SqlHelper(RuntimeEnvironment.application) val sqlHelper = SqlHelper(RuntimeEnvironment.application)
val addressRepo = AndroidAddressRepository(sqlHelper) val addressRepo = AndroidAddressRepository(sqlHelper)
repo = AndroidMessageRepository(sqlHelper, RuntimeEnvironment.application) val labelRepo = AndroidLabelRepository(sqlHelper, RuntimeEnvironment.application)
repo = AndroidMessageRepository(sqlHelper)
mockedInternalContext( mockedInternalContext(
cryptography = SpongyCryptography(), cryptography = SpongyCryptography(),
addressRepository = addressRepo, addressRepository = addressRepo,
messageRepository = repo, labelRepository = labelRepo,
port = 12345, messageRepository = repo,
connectionTTL = 10, port = 12345,
connectionLimit = 10 connectionTTL = 10,
connectionLimit = 10
) )
val tmp = BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK)) val tmp = BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK))
contactA = BitmessageAddress(tmp.address) contactA = BitmessageAddress(tmp.address)
@ -86,29 +88,16 @@ class AndroidMessageRepositoryTest : TestBase() {
identity = BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK)) identity = BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK))
addressRepo.save(identity) addressRepo.save(identity)
inbox = repo.getLabels(Label.Type.INBOX)[0] inbox = labelRepo.getLabels(Label.Type.INBOX)[0]
sent = repo.getLabels(Label.Type.SENT)[0] sent = labelRepo.getLabels(Label.Type.SENT)[0]
drafts = repo.getLabels(Label.Type.DRAFT)[0] drafts = labelRepo.getLabels(Label.Type.DRAFT)[0]
unread = repo.getLabels(Label.Type.UNREAD)[0] unread = labelRepo.getLabels(Label.Type.UNREAD)[0]
addMessage(contactA, identity, Plaintext.Status.RECEIVED, inbox, unread) addMessage(contactA, identity, Plaintext.Status.RECEIVED, inbox, unread)
addMessage(identity, contactA, Plaintext.Status.DRAFT, drafts) addMessage(identity, contactA, Plaintext.Status.DRAFT, drafts)
addMessage(identity, contactB, Plaintext.Status.DRAFT, unread) addMessage(identity, contactB, Plaintext.Status.DRAFT, unread)
} }
@Test
fun `ensure labels are retrieved`() {
val labels = repo.getLabels()
assertEquals(7, labels.size.toLong())
}
@Test
fun `ensure labels can be retrieved by type`() {
val labels = repo.getLabels(Label.Type.INBOX)
assertEquals(1, labels.size.toLong())
assertEquals("Inbox", labels[0].toString())
}
@Test @Test
fun `ensure messages can be found by label`() { fun `ensure messages can be found by label`() {
val messages = repo.findMessages(inbox) val messages = repo.findMessages(inbox)
@ -176,12 +165,12 @@ class AndroidMessageRepositoryTest : TestBase() {
@Test @Test
fun `ensure message can be saved`() { fun `ensure message can be saved`() {
val message = Plaintext.Builder(MSG) val message = Plaintext.Builder(MSG)
.IV(randomInventoryVector()) .IV(randomInventoryVector())
.from(identity) .from(identity)
.to(contactA) .to(contactA)
.message("Subject", "Message") .message("Subject", "Message")
.status(Plaintext.Status.DOING_PROOF_OF_WORK) .status(Plaintext.Status.DOING_PROOF_OF_WORK)
.build() .build()
repo.save(message) repo.save(message)
assertNotNull(message.id) assertNotNull(message.id)
@ -220,14 +209,14 @@ class AndroidMessageRepositoryTest : TestBase() {
@Test @Test
fun `ensure unacknowledged messages are found for resend`() { fun `ensure unacknowledged messages are found for resend`() {
val message = Plaintext.Builder(MSG) val message = Plaintext.Builder(MSG)
.IV(randomInventoryVector()) .IV(randomInventoryVector())
.from(identity) .from(identity)
.to(contactA) .to(contactA)
.message("Subject", "Message") .message("Subject", "Message")
.sent(UnixTime.now) .sent(UnixTime.now)
.status(Plaintext.Status.SENT) .status(Plaintext.Status.SENT)
.ttl(2) .ttl(2)
.build() .build()
message.updateNextTry() message.updateNextTry()
assertThat(message.retries, `is`(1)) assertThat(message.retries, `is`(1))
assertThat<Long>(message.nextTry, greaterThan(UnixTime.now)) assertThat<Long>(message.nextTry, greaterThan(UnixTime.now))
@ -265,57 +254,57 @@ class AndroidMessageRepositoryTest : TestBase() {
private fun addMessage(from: BitmessageAddress, to: BitmessageAddress, status: Plaintext.Status, vararg labels: Label): Plaintext { private fun addMessage(from: BitmessageAddress, to: BitmessageAddress, status: Plaintext.Status, vararg labels: Label): Plaintext {
val content = Message.Builder() val content = Message.Builder()
.subject("Subject") .subject("Subject")
.body("Message") .body("Message")
.build() .build()
return addMessage(from, to, content, status, *labels) return addMessage(from, to, content, status, *labels)
} }
private fun addMessage(from: BitmessageAddress, to: BitmessageAddress, private fun addMessage(from: BitmessageAddress, to: BitmessageAddress,
content: ExtendedEncoding, status: Plaintext.Status, vararg labels: Label): Plaintext { content: ExtendedEncoding, status: Plaintext.Status, vararg labels: Label): Plaintext {
val message = Plaintext.Builder(MSG) val message = Plaintext.Builder(MSG)
.IV(randomInventoryVector()) .IV(randomInventoryVector())
.from(from) .from(from)
.to(to) .to(to)
.message(content) .message(content)
.status(status) .status(status)
.labels(Arrays.asList(*labels)) .labels(Arrays.asList(*labels))
.build() .build()
repo.save(message) repo.save(message)
return message return message
} }
private fun storeConversation(): Plaintext { private fun storeConversation(): Plaintext {
val older = addMessage(identity, contactA, val older = addMessage(identity, contactA,
Message.Builder() Message.Builder()
.subject("hey there") .subject("hey there")
.body("does it work?") .body("does it work?")
.build(), .build(),
Plaintext.Status.SENT, sent) Plaintext.Status.SENT, sent)
val root = addMessage(identity, contactA, val root = addMessage(identity, contactA,
Message.Builder() Message.Builder()
.subject("new test") .subject("new test")
.body("There's a new test in town!") .body("There's a new test in town!")
.build(), .build(),
Plaintext.Status.SENT, sent) Plaintext.Status.SENT, sent)
addMessage(contactA, identity, addMessage(contactA, identity,
Message.Builder() Message.Builder()
.subject("Re: new test") .subject("Re: new test")
.body("Nice!") .body("Nice!")
.addParent(root) .addParent(root)
.build(), .build(),
Plaintext.Status.RECEIVED, inbox) Plaintext.Status.RECEIVED, inbox)
addMessage(contactA, identity, addMessage(contactA, identity,
Message.Builder() Message.Builder()
.subject("Re: new test") .subject("Re: new test")
.body("PS: it did work!") .body("PS: it did work!")
.addParent(root) .addParent(root)
.addParent(older) .addParent(older)
.build(), .build(),
Plaintext.Status.RECEIVED, inbox) Plaintext.Status.RECEIVED, inbox)
return repo.getMessage(root.id!!) return repo.getMessage(root.id!!)
} }

View File

@ -17,10 +17,7 @@
package ch.dissem.bitmessage.repository package ch.dissem.bitmessage.repository
import android.os.Build.VERSION_CODES.LOLLIPOP import android.os.Build.VERSION_CODES.LOLLIPOP
import ch.dissem.apps.abit.repository.AndroidAddressRepository import ch.dissem.apps.abit.repository.*
import ch.dissem.apps.abit.repository.AndroidMessageRepository
import ch.dissem.apps.abit.repository.AndroidProofOfWorkRepository
import ch.dissem.apps.abit.repository.SqlHelper
import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.ObjectMessage import ch.dissem.bitmessage.entity.ObjectMessage
import ch.dissem.bitmessage.entity.Plaintext import ch.dissem.bitmessage.entity.Plaintext
@ -64,10 +61,11 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
val sqlHelper = SqlHelper(RuntimeEnvironment.application) val sqlHelper = SqlHelper(RuntimeEnvironment.application)
addressRepo = AndroidAddressRepository(sqlHelper) addressRepo = AndroidAddressRepository(sqlHelper)
messageRepo = AndroidMessageRepository(sqlHelper, RuntimeEnvironment.application) messageRepo = AndroidMessageRepository(sqlHelper)
repo = AndroidProofOfWorkRepository(sqlHelper) repo = AndroidProofOfWorkRepository(sqlHelper)
mockedInternalContext( mockedInternalContext(
addressRepository = addressRepo, addressRepository = addressRepo,
labelRepository = AndroidLabelRepository(sqlHelper, RuntimeEnvironment.application),
messageRepository = messageRepo, messageRepository = messageRepo,
proofOfWorkRepository = repo, proofOfWorkRepository = repo,
cryptography = cryptography() cryptography = cryptography()

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.repository
import ch.dissem.bitmessage.BitmessageContext import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.InternalContext import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.Preferences
import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography
import ch.dissem.bitmessage.entity.BitmessageAddress import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.ObjectMessage import ch.dissem.bitmessage.entity.ObjectMessage
@ -40,42 +41,45 @@ open class TestBase {
@JvmStatic @JvmStatic
fun init() { fun init() {
mockedInternalContext( mockedInternalContext(
cryptography = SpongyCryptography(), cryptography = SpongyCryptography(),
proofOfWorkEngine = MultiThreadedPOWEngine() proofOfWorkEngine = MultiThreadedPOWEngine()
) )
} }
fun mockedInternalContext( fun mockedInternalContext(
cryptography: Cryptography = mock {}, cryptography: Cryptography = mock {},
inventory: Inventory = mock {}, inventory: Inventory = mock {},
nodeRegistry: NodeRegistry = mock {}, nodeRegistry: NodeRegistry = mock {},
networkHandler: NetworkHandler = mock {}, networkHandler: NetworkHandler = mock {},
addressRepository: AddressRepository = mock {}, addressRepository: AddressRepository = mock {},
messageRepository: MessageRepository = mock {}, labelRepository: LabelRepository = mock {},
proofOfWorkRepository: ProofOfWorkRepository = mock {}, messageRepository: MessageRepository = mock {},
proofOfWorkEngine: ProofOfWorkEngine = mock {}, proofOfWorkRepository: ProofOfWorkRepository = mock {},
customCommandHandler: CustomCommandHandler = mock {}, proofOfWorkEngine: ProofOfWorkEngine = mock {},
listener: BitmessageContext.Listener = mock {}, customCommandHandler: CustomCommandHandler = mock {},
labeler: Labeler = mock {}, listener: BitmessageContext.Listener = mock {},
port: Int = 0, labeler: Labeler = mock {},
connectionTTL: Long = 0, port: Int = 0,
connectionLimit: Int = 0 connectionTTL: Long = 0,
connectionLimit: Int = 0
) = spy(InternalContext( ) = spy(InternalContext(
cryptography, cryptography,
inventory, inventory,
nodeRegistry, nodeRegistry,
networkHandler, networkHandler,
addressRepository, addressRepository,
messageRepository, labelRepository,
proofOfWorkRepository, messageRepository,
proofOfWorkEngine, proofOfWorkRepository,
customCommandHandler, proofOfWorkEngine,
listener, customCommandHandler,
labeler, listener,
"/Jabit:TEST/", labeler,
port, Preferences().apply {
connectionTTL, this.port = port
connectionLimit this.connectionTTL = connectionTTL
this.connectionLimit = connectionLimit
}
)) ))
fun randomInventoryVector(): InventoryVector { fun randomInventoryVector(): InventoryVector {
@ -85,7 +89,7 @@ open class TestBase {
} }
private fun getResource(resourceName: String) = private fun getResource(resourceName: String) =
TestBase::class.java.classLoader.getResourceAsStream(resourceName) TestBase::class.java.classLoader.getResourceAsStream(resourceName)
private fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage { private fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage {
val data = getBytes(resourceName) val data = getBytes(resourceName)

View File

@ -1,5 +1,5 @@
buildscript { buildscript {
ext.kotlin_version = '1.1.60' ext.kotlin_version = '1.1.61'
ext.anko_version = '0.10.2' ext.anko_version = '0.10.2'
repositories { repositories {
jcenter() jcenter()