diff --git a/.gitignore b/.gitignore
index 66e553b6e..1a29acd2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@ build
*.iml
local.properties
.gradle
-gossipML/.cxx
**.project
**.settings
**.classpath
diff --git a/.gitmodules b/.gitmodules
index 8c9eb5b03..6991e3845 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
[submodule "kotlin-ipv8"]
path = kotlin-ipv8
url = https://github.com/Tribler/kotlin-ipv8.git
-[submodule "gossipML/superapp-essentia"]
- path = gossipML/superapp-essentia
- url = https://github.com/Tribler/superapp-essentia.git
diff --git a/README.md b/README.md
index 47969eb93..2a95f778d 100644
--- a/README.md
+++ b/README.md
@@ -77,16 +77,6 @@ Video 1: Load example. This uses a defaul
Video 2: Share track. Note: as a fresh magnet link is generated in this video, there is only 1 peer. For this reason it will be difficult to obtain the metadata of the magnet link (cold start issue, write about this in thesis) so the video stops there.
-### Federated, privacy-preserving music recommendations via gossiping
-
-This is a demonstration of machine learning which relies exclusively on edge computing. Music recommendation inside the MusicDAO is used to demonstrate gossip-based machine learning.
-
-Every time a user opens MusicDAO, they are asked to reload the page in order to get recommendations. The recommendation engine yields two recommendations made by two different models: a musical feature-based model and a collaborative filtering model. The collaborative filtering model is based on federated matrix factorization as introduced in [this paper](https://dmle.iais.fraunhofer.de/papers/hegedus2019decentralized.pdf). The feature-based models are from this [paper](https://arxiv.org/pdf/1109.1396.pdf), called Adaline and Pegasos. These models are trained on audio features extracted from music files with the [Essentia library](https://essentia.upf.edu/).
-
-
-The feature-based models are gossiped along random walks through the network. At each peer they are merged and re-trained on peer's local data. The matrix factorization model seeks to learn a factorization of the user-song matrix. This means that one of the two factors contains only information on how users generally rate each song. This matrix can then be gossiped around the network while a user's personal vector as well as their listening history are kept private.
- - [More about federated machine learning using gossiping for music recommendations](gossipML/README.md)
-
### Do you want to add your own app?
- [Adding your own app to the TrustChain Super App](doc/AppTutorial.md)
diff --git a/app/build.gradle b/app/build.gradle
index a545c4db3..97db41481 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -113,7 +113,6 @@ allprojects {
dependencies {
implementation project(':debug')
implementation project(':freedomOfComputing')
- implementation project(':peerchat')
implementation project(':eurotoken')
implementation project(':valuetransfer')
api(project(':common')) {
@@ -125,9 +124,6 @@ dependencies {
api(project(':musicdao')) {
exclude group: 'net.java.dev.jna'
}
- api(project(':gossipML')) {
- exclude group: 'net.java.dev.jna'
- }
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime:1.0-M1-1.4.0-rc'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4c115ff3f..1dcd46984 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -70,11 +70,6 @@
android:name="nl.tudelft.trustchain.debug.DebugActivity"
android:parentActivityName=".ui.dashboard.DashboardActivity" />
-
-
,
val disableImageTint: Boolean = false,
) {
- PEERCHAT(
- R.drawable.ic_chat_black_24dp,
- "PeerChat",
- R.color.purple,
- PeerChatActivity::class.java
- ),
DEBUG(
R.drawable.ic_bug_report_black_24dp,
"Debug",
diff --git a/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt b/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt
index 1d80e0763..5b7507123 100644
--- a/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt
+++ b/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt
@@ -62,13 +62,10 @@ import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
import nl.tudelft.trustchain.musicdao.core.dao.CoinCommunity
import nl.tudelft.trustchain.eurotoken.community.EuroTokenCommunity
import nl.tudelft.trustchain.eurotoken.db.TrustStore
-import nl.tudelft.trustchain.gossipML.RecommenderCommunity
-import nl.tudelft.trustchain.gossipML.db.RecommenderStore
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.community.PeerChatCommunity
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
import nl.tudelft.trustchain.valuetransfer.community.IdentityCommunity
import nl.tudelft.trustchain.valuetransfer.db.IdentityStore
-import nl.tudelft.gossipML.sqldelight.Database as MLDatabase
val Context.dataStore: DataStore by preferencesDataStore(name = "settings")
@@ -110,7 +107,6 @@ class TrustChainApplication : Application() {
createCoinCommunity(),
createDaoCommunity(),
createMusicCommunity(),
- createRecommenderCommunity(),
createIdentityCommunity(),
createFOCCommunity(),
),
@@ -339,23 +335,6 @@ class TrustChainApplication : Application() {
)
}
- @OptIn(DelicateCoroutinesApi::class) // TODO: Verify whether usage is correct.
- private fun createRecommenderCommunity(): OverlayConfiguration {
- val settings = TrustChainSettings()
- val musicDriver = AndroidSqliteDriver(Database.Schema, this, "music.db")
- val musicStore = TrustChainSQLiteStore(Database(musicDriver))
- val driver = AndroidSqliteDriver(MLDatabase.Schema, this, "recommend.db")
- val database = MLDatabase(driver)
-
- val recommendStore = RecommenderStore.getInstance(musicStore, database)
- recommendStore.essentiaJob = GlobalScope.launch { recommendStore.addAllLocalFeatures() }
- val randomWalk = RandomWalk.Factory()
- return OverlayConfiguration(
- RecommenderCommunity.Factory(recommendStore, settings, musicStore),
- listOf(randomWalk)
- )
- }
-
private fun createFOCCommunity(): OverlayConfiguration {
val randomWalk = RandomWalk.Factory()
return OverlayConfiguration(
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index 04f33e5b3..025bcdf55 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -13,17 +13,10 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
- app:layout_constraintBottom_toTopOf="@id/bottomNavigation"
+ app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph_dashboard" />
-
diff --git a/build.gradle b/build.gradle
index 72c0b27b4..e85726cf9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -32,7 +32,6 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
- // TODO: Find a way to move this to trustchain-trader (gossipML relies on this too)
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
diff --git a/gossipML/.gitignore b/gossipML/.gitignore
deleted file mode 100644
index 42afabfd2..000000000
--- a/gossipML/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/gossipML/README.md b/gossipML/README.md
deleted file mode 100644
index a3c52873c..000000000
--- a/gossipML/README.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Federated, privacy-preserving music recommendations via gossiping
-
-This module contains a collection of recommendation models built on top of [IPv8](https://github.com/MattSkala/kotlin-ipv8)
-
-Every time a user opens MusicDAO, they are asked to reload the page in order to get recommendations. This is done with the purpose of getting recommendations on request and avoid recommending meaningless data when there is a lack of local songs (training data).
-
-
-
-## Overview
-
-The recommendation engine yields two recommendations made by two different models: a musical feature-based model and a collaborative filtering model.
-The idea is that the two models will capture different aspects in the patterns of a user's musical preference and so provide distinct recommendations.
-
-The collaborative filtering model is based on federated matrix factorization as introduced in [this paper](https://dmle.iais.fraunhofer.de/papers/hegedus2019decentralized.pdf).
-The feature-based models are from this [paper](https://arxiv.org/pdf/1109.1396.pdf), called Adaline and Pegasos.
-These models are trained on audio features extracted from music files with the [Essentia library](https://essentia.upf.edu/).
-
-All peers in the network have their own local model instances for both model types.
-The local models are fine-tuned before making predictions to be personalized to a user's listening history.
-Peers gossip models and features around the network to increase the predictive performance for everyone.
-This is done in such a way that no private user data (listening history) is ever exchanged with peers.
-
-Local Essentia features are also gossiped among peers in order to have features for recommendations of 'unseen' songs.
-This also prevents duplicate work, as the Essentia features are relatively costly to calculate on mobile phones.
-
-The two recommendation models follow slightly different gossiping strategies (according to the algorithms described in their respective papers / sections below). The figure below gives an overview of each model's propagation through the network.
-
-
-
-The feature-based models are gossiped along random walks through the network. At each peer they are merged and re-trained on peer's local data.
-
-The matrix factorization model seeks to learn a factorization of the user-song matrix. This means that one of the two factors contains only information on how users generally rate each song. This matrix can then be gossiped around the network while a user's personal vector as well as their listening history are kept private.
-
-## Module structure
-
-The general structure of the `gossipML` module can be seen below. The RecommenderCommunity is responsible for communications between peers. The RecommenderStore handles local data operations and preprocessing (audio features, model weights, listening history, etc.). The models are actually executed from the RecommendationFragment, which is in charge of user interaction with the UI.
-
-
-
-## Model details
-
-### Feature-based models
-
-Adaline (ADAptive LInear NEuron) is a single layer neural network that uses continuous predicted values from the net input.
-Due to the use of continuous predicted labels taken before activation function, Adaline is capable of measuring the extent by which predictions were right/wrong.
-
-Pegasos (Primal Estimated sub-Gradient SOlver for SVM) is a sub-gradient descent algorithm for solving optimization problems cast by support vector machines. This is the feature-based model in use in the network.
-
-Pseudocode for Pegasos and Adaline models is shown below:
-
-
-
-More formally, learning rule for Pegasos is defined as:
-
-
-And learning rule for Adaline is defined as:
-
-
-### Collaborative filtering models
-
-Matrix Factorization model is collaborative filtering model that bases recommendations on private logs of user activity (song history) by means of low-rank matrix decomposition.
-In the private model, every matrix row corresponds to some user private information on music history.
-This private matrix is then approximatelly decomposed into matrices, X and Y, with Y shared among the users.
-
-The general error that the model tries to minimize is:
-,
-where the bias (c and b) represent the overall average score of the given user/song, and X and Y represent relative differences.
-
-Below is the pseudocode for the update rule corresponding to the above loss function:
-
-
-
-Merging of gossiped models and local models happens via age-weighted averaging. This gives more weight to rows of the song factor matrix which have been updated more often (and so are probably more robust overall).
-
-
-
-## Model performance
-
-So far, the MatrixFactorization model showed to be pretty reliable on example tests.
-
-Unfortunatelly, the feature-based models still lack proper pre-training/fine-tuning.
-We have selected Essentia features that most distinctly separated a set of example albums (the features of which can be found in the 'superapp-essentia/test/res' folder).
-Nevertheless, prediction are not able to achieve high accuracy and we lack user preference data in order to properly group music data and tune the models.
-
-One thing that we have observed during testing is that upon merging the global walking model, local models weights were being overpowered when using a 50-50 weighting.
-Thus, contrary to the paper, we average two models with 90% local model and only 10% walking model in order to keep local models tuned to local data.
-In our case, we are not interested in one global model that can predict likes for everyone, but rather many local ones which are informed of useful feature representations by the global model.
\ No newline at end of file
diff --git a/gossipML/build.gradle b/gossipML/build.gradle
deleted file mode 100644
index 5cbf9f023..000000000
--- a/gossipML/build.gradle
+++ /dev/null
@@ -1,154 +0,0 @@
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
-apply plugin: 'org.jlleitschuh.gradle.ktlint'
-apply plugin: 'kotlinx-serialization'
-apply plugin: 'com.squareup.sqldelight'
-
-sqldelight {
- Database {
- packageName = "nl.tudelft.gossipML.sqldelight"
- sourceFolders = ["sqldelight"]
- schemaOutputDirectory = file("src/main/sqldelight/databases")
- }
-}
-
-repositories {
- mavenCentral()
- google()
-}
-
-android {
- compileSdkVersion 33
-
- useLibrary 'android.test.base'
- useLibrary 'android.test.mock'
-
- buildFeatures{
- viewBinding = true
- }
-
- defaultConfig {
- minSdkVersion 22
- targetSdkVersion 33
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-
- externalNativeBuild {
- cmake {
- path "src/main/cpp/CMakeLists.txt"
- version "3.22.1"
- }
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = "1.8"
- }
-
- testOptions {
- unitTests.returnDefaultValues = true
- }
- packagingOptions {
- resources {
- excludes += ['META-INF/DEPENDENCIES', 'META-INF/LICENSE', 'META-INF/LICENSE.txt', 'META-INF/license.txt', 'META-INF/NOTICE', 'META-INF/NOTICE.txt', 'META-INF/notice.txt', 'META-INF/ASL2.0', 'META-INF/*.kotlin_module']
- }
- }
-
-
- sourceSets {
- androidTest {
- java.srcDirs = ['src/androidTest/java']
- res.srcDirs = ['src/androidTest/res']
- }
- }
-
- splits {
- abi {
- enable true
- reset()
- include 'arm64-v8a', 'x86', 'armeabi-v7a', 'x86_64'
- universalApk true
- }
- }
- ndkVersion '21.4.7075529'
- namespace 'nl.tudelft.trustchain.gossipML'
-}
-
-dependencies {
- implementation project(':common')
- api(project(':ipv8-android')){
- exclude group: 'net.java.dev.jna'
- exclude group: 'org.bouncycastle'
- }
- implementation project(':musicdao-datafeeder')
-
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation "androidx.preference:preference-ktx:1.1.1"
- implementation 'androidx.core:core-ktx:1.9.0'
-// implementation "androidx.collection:collection-ktx:1.3.2"
- implementation 'androidx.legacy:legacy-support-v4:1.0.0'
- implementation 'com.google.android.material:material:1.3.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
- implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
- implementation 'com.google.android:flexbox:2.0.1'
-
- implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime:1.0-M1-1.4.0-rc'
-
- // BitTorrent/Libtorrent libraries
- implementation 'com.turn:ttorrent-core:1.5'
-
- implementation 'com.google.android.exoplayer:exoplayer-core:2.10.5'
- implementation 'com.google.android.exoplayer:exoplayer-dash:2.10.5'
- implementation 'com.google.android.exoplayer:exoplayer-ui:2.10.5'
- implementation 'com.google.android.exoplayer:exoplayer-hls:2.10.5'
-
- // Cryptocurrency integration
- implementation('org.bitcoinj:bitcoinj-core:0.15.10')
- implementation 'org.knowm.xchange:xchange-parent:5.0.1'
- implementation 'org.knowm.xchange:xchange-binance:5.0.1'
-
- // Testing
- testImplementation 'junit:junit:4.12'
- testImplementation "io.mockk:mockk:1.9.3"
- testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.7'
- testImplementation "org.robolectric:robolectric:3.4.2"
- testImplementation "com.goterl:lazysodium-java:5.1.4"
- testImplementation 'androidx.core:core-ktx:1.9.0'
-
- // Testing and generating example data
- implementation "com.squareup.sqldelight:sqlite-driver:$sqldelight_version"
-
- // Reading MP3 metadata
- implementation 'com.mpatric:mp3agic:0.9.1'
-
- // Logging
- implementation 'io.github.microutils:kotlin-logging:1.7.7'
-
- implementation "com.github.kotlin-graphics:kotlin-unsigned:v2.1"
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
- kotlinOptions.freeCompilerArgs += [
- "-opt-in=kotlin.Experimental,kotlin.ExperimentalUnsignedTypes"
- ]
-}
diff --git a/gossipML/consumer-rules.pro b/gossipML/consumer-rules.pro
deleted file mode 100644
index e69de29bb..000000000
diff --git a/gossipML/docs/etc/essentia_test_albums_stats.ods b/gossipML/docs/etc/essentia_test_albums_stats.ods
deleted file mode 100644
index 8bc4a061c..000000000
Binary files a/gossipML/docs/etc/essentia_test_albums_stats.ods and /dev/null differ
diff --git a/gossipML/docs/etc/recording.gif b/gossipML/docs/etc/recording.gif
deleted file mode 100644
index 1bfc6ac6a..000000000
Binary files a/gossipML/docs/etc/recording.gif and /dev/null differ
diff --git a/gossipML/docs/imgs/ada_rule.png b/gossipML/docs/imgs/ada_rule.png
deleted file mode 100644
index 39eb59e56..000000000
Binary files a/gossipML/docs/imgs/ada_rule.png and /dev/null differ
diff --git a/gossipML/docs/imgs/adaline_alg.png b/gossipML/docs/imgs/adaline_alg.png
deleted file mode 100644
index e62eb9e8f..000000000
Binary files a/gossipML/docs/imgs/adaline_alg.png and /dev/null differ
diff --git a/gossipML/docs/imgs/gossipML_diagram.png b/gossipML/docs/imgs/gossipML_diagram.png
deleted file mode 100644
index a618f2da0..000000000
Binary files a/gossipML/docs/imgs/gossipML_diagram.png and /dev/null differ
diff --git a/gossipML/docs/imgs/mf.png b/gossipML/docs/imgs/mf.png
deleted file mode 100644
index a4b327a5e..000000000
Binary files a/gossipML/docs/imgs/mf.png and /dev/null differ
diff --git a/gossipML/docs/imgs/mf_alg.png b/gossipML/docs/imgs/mf_alg.png
deleted file mode 100644
index c3664781b..000000000
Binary files a/gossipML/docs/imgs/mf_alg.png and /dev/null differ
diff --git a/gossipML/docs/imgs/mf_merge.png b/gossipML/docs/imgs/mf_merge.png
deleted file mode 100644
index 555f3d5b3..000000000
Binary files a/gossipML/docs/imgs/mf_merge.png and /dev/null differ
diff --git a/gossipML/docs/imgs/overview.png b/gossipML/docs/imgs/overview.png
deleted file mode 100644
index 2919aaa05..000000000
Binary files a/gossipML/docs/imgs/overview.png and /dev/null differ
diff --git a/gossipML/docs/imgs/pegasos_rule.png b/gossipML/docs/imgs/pegasos_rule.png
deleted file mode 100644
index 45c2aef34..000000000
Binary files a/gossipML/docs/imgs/pegasos_rule.png and /dev/null differ
diff --git a/gossipML/docs/imgs/recommendations.png b/gossipML/docs/imgs/recommendations.png
deleted file mode 100644
index d1189a2b2..000000000
Binary files a/gossipML/docs/imgs/recommendations.png and /dev/null differ
diff --git a/gossipML/docs/imgs/start_recommendations.png b/gossipML/docs/imgs/start_recommendations.png
deleted file mode 100644
index 91e6faf12..000000000
Binary files a/gossipML/docs/imgs/start_recommendations.png and /dev/null differ
diff --git a/gossipML/proguard-rules.pro b/gossipML/proguard-rules.pro
deleted file mode 100644
index 8491f82d2..000000000
--- a/gossipML/proguard-rules.pro
+++ /dev/null
@@ -1,18 +0,0 @@
--keepattributes *Annotation*, InnerClasses
--dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
-
-# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
--keepclassmembers class kotlinx.serialization.json.** {
- *** Companion;
-}
--keepclasseswithmembers class kotlinx.serialization.json.** {
- kotlinx.serialization.KSerializer serializer(...);
-}
-
--keep,includedescriptorclasses class nl.tudelft.trustchain.gossipML.**$$serializer { *; } # <-- change package name to your app's
--keepclassmembers class nl.tudelft.trustchain.gossipML.** { # <-- change package name to your app's
- *** Companion;
-}
--keepclasseswithmembers class nl.tudelft.trustchain.gossipML.** { # <-- change package name to your app's
- kotlinx.serialization.KSerializer serializer(...);
-}
\ No newline at end of file
diff --git a/gossipML/src/androidTest/java/EssentiaTest.kt b/gossipML/src/androidTest/java/EssentiaTest.kt
deleted file mode 100644
index d7207eba6..000000000
--- a/gossipML/src/androidTest/java/EssentiaTest.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import nl.tudelft.trustchain.gossipML.Essentia
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class EssentiaTest {
-
- @Test
- fun testEssentia() {
- var filepath = File("superapp-essentia/test/assets/GrayMicRecords-LofiDream.mp3").absolutePath
- assertEquals(0, Essentia.extractData(filepath, filepath.replace(".mp3", ".json")))
-
- filepath = File("superapp-essentia/test/assets/Helen&Shanna-Saudades.mp3").absolutePath
- assertEquals(0, Essentia.extractData(filepath, filepath.replace(".mp3", ".json")))
- }
-}
diff --git a/gossipML/src/main/AndroidManifest.xml b/gossipML/src/main/AndroidManifest.xml
deleted file mode 100644
index 8b84ec926..000000000
--- a/gossipML/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/gossipML/src/main/cpp/CMakeLists.txt b/gossipML/src/main/cpp/CMakeLists.txt
deleted file mode 100644
index 1efe2539c..000000000
--- a/gossipML/src/main/cpp/CMakeLists.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-cmake_minimum_required(VERSION 3.22.1)
-
-project("superappessentia")
-
-add_library(superappessentia
- SHARED
- essentia_jni.cpp)
-
-add_library(essentia STATIC IMPORTED)
-add_library(avformat STATIC IMPORTED)
-add_library(avcodec STATIC IMPORTED)
-add_library(avutil STATIC IMPORTED)
-add_library(avresample STATIC IMPORTED)
-add_library(samplerate STATIC IMPORTED)
-add_library(mp3lame STATIC IMPORTED)
-add_library(chromaprint STATIC IMPORTED)
-add_library(tag STATIC IMPORTED)
-add_library(yaml STATIC IMPORTED)
-
-set_target_properties( essentia
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libessentia.a)
-
-set_target_properties( avformat
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libavformat.a)
-
-set_target_properties( avcodec
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libavcodec.a)
-
-set_target_properties( avutil
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libavutil.a)
-
-set_target_properties( avresample
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libavresample.a)
-
-set_target_properties( samplerate
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libsamplerate.a)
-
-set_target_properties( mp3lame
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libmp3lame.a)
-
-set_target_properties( chromaprint
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libchromaprint.a)
-
-set_target_properties( tag
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libtag.a)
-
-set_target_properties( yaml
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/../../../superapp-essentia/libs/${ANDROID_ABI}/libyaml.a)
-
-include_directories(${CMAKE_SOURCE_DIR}/../../../superapp-essentia/include)
-include_directories(${CMAKE_SOURCE_DIR}/../../../superapp-essentia/include/eigen3)
-
-find_library(log-lib log)
-find_library(math-lib m)
-find_library(zlib z)
-
-target_link_libraries(superappessentia essentia yaml avresample avformat avcodec avutil mp3lame
- chromaprint tag samplerate ${log-lib} ${math-lib} ${zlib})
\ No newline at end of file
diff --git a/gossipML/src/main/cpp/essentia_jni.cpp b/gossipML/src/main/cpp/essentia_jni.cpp
deleted file mode 100644
index 831372c88..000000000
--- a/gossipML/src/main/cpp/essentia_jni.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace std;
-using namespace essentia;
-using namespace essentia::standard;
-
-void setExtractorDefaultOptions(Pool &options) {
- options.set("outputFrames", false);
- options.set("outputFormat", "json");
- options.set("requireMbid", false);
- options.set("indent", 2);
-
- options.set("highlevel.inputFormat", "json");
-}
-
-void setExtractorOptions(const std::string& filename, Pool& options) {
- if (filename.empty()) return;
-
- Pool opts;
- Algorithm * yaml = AlgorithmFactory::create("YamlInput", "filename", filename);
- yaml->output("pool").set(opts);
- yaml->compute();
- delete yaml;
- options.merge(opts, "replace");
-}
-
-void mergeValues(Pool& pool, Pool& options) {
- string mergeKeyPrefix = "mergeValues";
- vector keys = options.descriptorNames(mergeKeyPrefix);
-
- for (int i=0; i<(int) keys.size(); ++i) {
- keys[i].replace(0, mergeKeyPrefix.size()+1, "");
- pool.set(keys[i], options.value(mergeKeyPrefix + "." + keys[i]));
- }
-}
-
-void outputToFile(Pool& pool, const string& outputFilename, Pool& options) {
- cerr << "Writing results to file " << outputFilename << endl;
- int indent = (int)options.value("indent");
-
- string format = options.value("outputFormat");
- Algorithm* output = AlgorithmFactory::create("YamlOutput",
- "filename", outputFilename,
- "doubleCheck", true,
- "format", format,
- "writeVersion", false,
- "indent", indent);
- output->input("pool").set(pool);
- output->compute();
- delete output;
-}
-
-jmp_buf env;
-
-void on_sigabrt (int signum)
-{
- signal (signum, SIG_DFL);
- __android_log_write(ANDROID_LOG_ERROR, "Essentia Android", "SIGSEGV or SIGABRT :(");
- longjmp (env, 1);
-}
-
-int essentia_main(string audioFilename, string outputFilename) {
- // Returns: 1 on essentia error
-
- try {
- essentia::init();
- setDebugLevel(ENone);
-
- cout.precision(10);
-
- Pool options;
- setExtractorDefaultOptions(options);
- setExtractorOptions("", options);
-
-
- Algorithm* extractor = AlgorithmFactory::create("MusicExtractor");
- Pool results, resultsFrames;
-
- extractor->input("filename").set(audioFilename);
- extractor->output("results").set(results);
- extractor->output("resultsFrames").set(resultsFrames);
-
- if (setjmp (env) == 0) {
- signal(SIGABRT, &on_sigabrt);
- signal(SIGSEGV, &on_sigabrt);
- extractor->compute();
- }
- else {
- return 1;
- }
-
- mergeValues(results, options);
-
- outputToFile(results, outputFilename, options);
- delete extractor;
- essentia::shutdown();
- }
- catch (EssentiaException& e) {
- __android_log_write(ANDROID_LOG_ERROR, "Essentia Android", e.what());
- return 1;
- }
- catch (const std::bad_alloc& e) {
- __android_log_write(ANDROID_LOG_ERROR, "Essentia Android", "bad_alloc exception: Out of memory");
- __android_log_write(ANDROID_LOG_ERROR, "Essentia Android", e.what());
- return 1;
- }
-
- return 0;
-}
-
-string convertJStringToString(JNIEnv *env, jstring str) {
- jboolean is_copy;
- const char *CString = env->GetStringUTFChars(str, &is_copy);
- return std::string(CString);
-}
-
-extern "C"
-JNIEXPORT jint JNICALL
-Java_nl_tudelft_trustchain_gossipML_Essentia_extractData(JNIEnv *env, jclass, jstring input_path, jstring output_path) {
- std::string input = convertJStringToString(env, input_path);
- std::string output = convertJStringToString(env, output_path);
- int returnCode = essentia_main(input, output);
- return returnCode;
-}
\ No newline at end of file
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/Essentia.java b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/Essentia.java
deleted file mode 100644
index 0dcbb1b4d..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/Essentia.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package nl.tudelft.trustchain.gossipML;
-
-/**
- * Bridge between Essentia music library and federated ml library
- */
-public final class Essentia {
- static {
- System.loadLibrary("superappessentia");
- }
- public native static int extractData(String inputPath, String outputPath);
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt
deleted file mode 100644
index 10e947630..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt
+++ /dev/null
@@ -1,307 +0,0 @@
-package nl.tudelft.trustchain.gossipML
-
-import android.util.Log
-import nl.tudelft.ipv8.Overlay
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainCommunity
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainCrawler
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainSettings
-import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainStore
-import nl.tudelft.ipv8.messaging.Packet
-import nl.tudelft.trustchain.gossipML.db.RecommenderStore
-import nl.tudelft.trustchain.gossipML.ipv8.FeaturesExchangeMessage
-import nl.tudelft.trustchain.gossipML.ipv8.ModelExchangeMessage
-import nl.tudelft.trustchain.gossipML.ipv8.RequestModelMessage
-import nl.tudelft.trustchain.gossipML.models.Model
-import nl.tudelft.trustchain.gossipML.models.OnlineModel
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.MatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.PublicMatrixFactorization
-import java.util.*
-import kotlin.random.Random
-
-/**
- * Recommender community for gossiping recommendation models and songs features among peers
- */
-@ExperimentalUnsignedTypes
-open class RecommenderCommunity(
- val recommendStore: RecommenderStore,
- settings: TrustChainSettings,
- database: TrustChainStore,
- crawler: TrustChainCrawler = TrustChainCrawler()
-) : TrustChainCommunity(settings, database, crawler) {
- override val serviceId = "29384902d2938f34872398758cf7ca9238ccc333"
-
- class Factory(
- private val recommendStore: RecommenderStore,
- private val settings: TrustChainSettings,
- private val database: TrustChainStore,
- private val crawler: TrustChainCrawler = TrustChainCrawler()
- ) : Overlay.Factory(RecommenderCommunity::class.java) {
- override fun create(): RecommenderCommunity {
- return RecommenderCommunity(recommendStore, settings, database, crawler)
- }
- }
-
- init {
- messageHandlers[MessageId.MODEL_EXCHANGE_MESSAGE] = ::onModelExchange
- messageHandlers[MessageId.REQUEST_MODEL] = ::onModelRequest
- messageHandlers[MessageId.EXCHANGE_FEATURES] = ::onFeaturesExchange
- }
-
- /**
- * on initialization initiate walking models
- */
- override fun load() {
- super.load()
-
- if (Random.nextInt(0, 1) == 0) initiateWalkingModel()
- }
-
- /**
- * exchange extracted features for local songs with other peers
- */
- fun exchangeMyFeatures() {
- try {
- Log.i("Recommend", "Send my features")
- performLocalFeaturesExchange()
- } catch (e: Exception) {
- Log.i("Recommend", "Sending local features failed")
- e.printStackTrace()
- }
- }
-
- /**
- * Load / create walking models (feature based and collaborative filtering)
- */
- fun initiateWalkingModel() {
- try {
- Log.i("Recommend", "Initiate random walk")
- performRemoteModelExchange(recommendStore.getLocalModel("Pegasos"))
- performRemoteModelExchange(recommendStore.getLocalModel("MatrixFactorization"))
- } catch (e: Exception) {
- Log.i("Recommend", "Random walk failed")
- e.printStackTrace()
- }
- }
-
- /**
- * Exchange walking model with other peers
- *
- * @param model walking model
- * @param ttl ttl of a model packet
- * @param originPublicKey public key
- * @return amount of peers to whom model has been sent
- */
- @ExperimentalUnsignedTypes
- fun performRemoteModelExchange(
- model: Model,
- ttl: UInt = 2u,
- originPublicKey: ByteArray = myPeer.publicKey.keyToBin()
- ): Int {
- val maxPeersToAsk = 5
- var count = 0
- for ((index, peer) in getPeers().withIndex()) {
- if (index >= maxPeersToAsk) break
- val packet = serializePacket(
- MessageId.MODEL_EXCHANGE_MESSAGE,
- ModelExchangeMessage(originPublicKey, ttl, model.name, model)
- )
- Log.i("Recommend", "Sending models to ${peer.address}")
- send(peer, packet)
- count += 1
- }
- return count
- }
-
- /**
- * Exchange local song's features with other peers
- */
- private fun performLocalFeaturesExchange() {
- val myFeatures = recommendStore.getMyFeatures()
- for (feature in myFeatures) {
- feature.songFeatures?.let { performFeaturesExchange(feature.key, it) }
- }
- }
-
- /**
- * exchange packet with song features with other peers
- *
- * @param identifier song indentifier
- * @param features song features
- * @param ttl packet ttl
- * @param originPublicKey original public key
- * @return amount of peers to whom features has been sent
- */
- @ExperimentalUnsignedTypes
- fun performFeaturesExchange(
- identifier: String,
- features: String,
- ttl: UInt = 2u,
- originPublicKey: ByteArray = myPeer.publicKey.keyToBin()
- ): Int {
- val maxPeersToAsk = 5
- var count = 0
- for ((index, peer) in getPeers().withIndex()) {
- if (index >= maxPeersToAsk) break
- val packet = serializePacket(
- MessageId.EXCHANGE_FEATURES,
- FeaturesExchangeMessage(originPublicKey, ttl, identifier, features)
- )
- Log.i("Recommend", "Sending features to ${peer.address}")
- send(peer, packet)
- count += 1
- }
- Log.i("Recommend", "Features exchanged with $count peer(s)")
- return count
- }
-
- /**
- * Handling incoming walking model by updating it with local features
- * and merging with local model
- *
- * @param packet incoming packet with walking model
- */
- @ExperimentalUnsignedTypes
- fun onModelExchange(packet: Packet) {
- Log.i("Recommend", "Some packet with model received")
- val (_, payload) = packet.getAuthPayload(ModelExchangeMessage)
-
- // packet contains model type and weights from peer
- @Suppress("DEPRECATION")
- val modelType = payload.modelType.toLowerCase(Locale.ROOT)
- var peerModel = payload.model
- Log.i("Recommend", "Walking model is de-packaged")
-
- var localModel = recommendStore.getLocalModel(modelType)
-
- if (modelType == "Adaline" || modelType == "Pegasos") {
- val data = recommendStore.getLocalSongData()
- val songFeatures = data.first
- val playcounts = data.second
- val models = mergeFeatureModel(
- localModel as OnlineModel,
- peerModel as OnlineModel,
- songFeatures,
- playcounts
- )
- Log.i("Recommend", "Walking an random models are merged")
- recommendStore.storeModelLocally(models.first)
- if (payload.checkTTL()) performRemoteModelExchange(models.second)
- } else {
- peerModel = peerModel as PublicMatrixFactorization
- localModel = localModel as MatrixFactorization
- if (localModel.songFeatures.size == 0) {
- localModel = MatrixFactorization(peerModel.peerFeatures)
- localModel.updateRatings(
- recommendStore.getSongIds().zip(recommendStore.getPlaycounts()).toMap()
- .toSortedMap()
- )
- recommendStore.storeModelLocally(localModel)
- val maxPeersToAsk = 5
- var count = 0
- for ((index, peer) in getPeers().withIndex()) {
- if (index >= maxPeersToAsk) break
- send(
- peer,
- serializePacket(
- MessageId.REQUEST_MODEL,
- RequestModelMessage(
- myPeer.publicKey.keyToBin(),
- 2u,
- "MatrixFactorization"
- )
- )
- )
- count += 1
- }
- Log.i("Recommend", "Model request sent to $count peer(s)")
- } else {
- Log.i("Recommend", "Merging MatrixFactorization")
- localModel.updateRatings(
- recommendStore.getSongIds().zip(recommendStore.getPlaycounts()).toMap()
- .toSortedMap()
- )
- localModel.merge(peerModel.peerFeatures)
- recommendStore.storeModelLocally(localModel)
- Log.i("Recommend", "Stored new MatrixFactorization")
- if (payload.checkTTL()) performRemoteModelExchange(localModel)
- }
- }
- }
-
- /**
- * Handling incomingpacket with song features
- * by adding 'unseen' features to the local database
- *
- * @param packet incoming packet with song features
- */
- @ExperimentalUnsignedTypes
- fun onFeaturesExchange(packet: Packet) {
- Log.i("Recommend", "Some packet with features received")
- val (_, payload) = packet.getAuthPayload(FeaturesExchangeMessage)
-
- // packet contains model type and weights from peer
- @Suppress("DEPRECATION")
- val songIdentifier = payload.songIdentifier.toLowerCase(Locale.ROOT)
- val features = payload.features
- Log.i("Recommend", "Song features are de-packaged")
-
- recommendStore.addNewFeatures(songIdentifier, features)
- if (payload.checkTTL()) performFeaturesExchange(songIdentifier, features)
- }
-
- /**
- * Handle model request from other peers
- *
- * @param packet request model packet
- */
- private fun onModelRequest(packet: Packet) {
- Log.i("Recommend", "Model request received")
- val (_, payload) = packet.getAuthPayload(ModelExchangeMessage)
- @Suppress("DEPRECATION")
- val modelType = payload.modelType.toLowerCase(Locale.ROOT)
- val model = recommendStore.getLocalModel(modelType)
- send(
- packet.source,
- serializePacket(
- MessageId.MODEL_EXCHANGE_MESSAGE,
- ModelExchangeMessage(myPeer.publicKey.keyToBin(), 1u, model.name, model)
- )
- )
- }
-
- /**
- * Handle local and walking model merging and updating
- *
- * @param incomingModel walking model
- * @param localModel local model
- * @param features local features
- * @param labels local labels
- * @return pair of merged local and merged walking models
- */
- private fun mergeFeatureModel(
- incomingModel: OnlineModel,
- localModel: OnlineModel,
- features: Array>,
- labels: IntArray
- ):
- Pair {
- localModel.merge(incomingModel)
- incomingModel.update(features, labels.map { it.toDouble() }.toTypedArray())
- return Pair(localModel, incomingModel)
- }
-
- object MessageId {
- val MODEL_EXCHANGE_MESSAGE: Int
- get() {
- return 27
- }
- val REQUEST_MODEL: Int
- get() {
- return 40
- }
- val EXCHANGE_FEATURES: Int
- get() {
- return 99
- }
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/db/RecommenderStore.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/db/RecommenderStore.kt
deleted file mode 100644
index e57feae6b..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/db/RecommenderStore.kt
+++ /dev/null
@@ -1,583 +0,0 @@
-package nl.tudelft.trustchain.gossipML.db
-
-import android.annotation.SuppressLint
-import android.util.Log
-import nl.tudelft.trustchain.musicdaodatafeeder.AudioFileFilter
-import com.mpatric.mp3agic.Mp3File
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-import nl.tudelft.gossipML.sqldelight.Database
-import nl.tudelft.gossipML.sqldelight.Features
-import nl.tudelft.gossipML.sqldelight.Unseen_features
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
-import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainSQLiteStore
-import nl.tudelft.trustchain.gossipML.Essentia
-import nl.tudelft.trustchain.gossipML.models.Model
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.MatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.feature_based.Adaline
-import nl.tudelft.trustchain.gossipML.models.feature_based.Pegasos
-import org.json.JSONObject
-import java.io.File
-import kotlin.math.log10
-
-/**
- * Handles database operations and preprocessing for local models and features
- */
-open class RecommenderStore(
- private val musicStore: TrustChainSQLiteStore,
- private val database: Database
-) {
- lateinit var key: ByteArray
- @SuppressLint("SdCardPath")
- private val musicDir = File("/data/user/0/nl.tudelft.trustchain/cache/")
- lateinit var essentiaJob: Job
- private var highVarianceFeatureLabels = arrayOf(3, 62, 162, 223, 130, 140)
- // 2 block features + cherry-picked essentia features
- private val totalAmountFeatures = highVarianceFeatureLabels.size + 2
-
- /**
- * save local model to the database
- *
- * @param model model to be stored
- */
- fun storeModelLocally(model: Model) {
- if (model.name == "MatrixFactorization") {
- database.dbModelQueries.addModel(
- name = model.name,
- type = model.name,
- parameters = (model as MatrixFactorization).serialize(private = true)
- )
- } else if (model.name == "Pegasos") {
- database.dbModelQueries.addModel(
- name = model.name,
- type = model.name,
- parameters = (model as Pegasos).serialize()
- )
- } else {
- database.dbModelQueries.addModel(
- name = model.name,
- type = model.name,
- parameters = (model as Adaline).serialize()
- )
- }
- }
-
- /**
- * loads model from the local database using name is a key
- *
- * @param name model name
- * @return loaded model
- */
- fun getLocalModel(name: String): Model {
- Log.i("Recommend", "Loading $name")
- val dbModel = database.dbModelQueries.getModel(name).executeAsOneOrNull()
- val model: Model
- if (name == "Adaline") {
- if (dbModel != null) {
- Log.i("Recommend", "Load existing local model")
- model = Json.decodeFromString(dbModel.parameters)
- } else {
- model = Adaline(0.1, totalAmountFeatures)
- Log.i("Recommend", "Initialized local model")
- Log.i("Recommend", model.name)
- }
- } else if (name == "Pegasos") {
- if (dbModel != null) {
- Log.i("Recommend", "Load existing local model")
- model = Json.decodeFromString(dbModel.parameters)
- } else {
- model = Pegasos(0.01, totalAmountFeatures, 10)
- Log.i("Recommend", "Initialized local model")
- Log.i("Recommend", model.name)
- }
- } else {
- if (dbModel != null) {
- Log.i("Recommend", "Load existing local model")
- model = Json.decodeFromString(dbModel.parameters)
- } else {
- model = MatrixFactorization(Array(0) { "" }.zip(Array(0) { 0.0 }).toMap().toSortedMap())
- Log.i("Recommend", "Initialized local model")
- Log.i("Recommend", model.name)
- }
- }
- /**
- * we are trying to 'bias' our model with local preferences such that it is tuned for local user
- * thus, we re-train it on local dataset every time we load the model
- */
- val trainingData = getLocalSongData()
- if (trainingData.first.isNotEmpty()) {
- if (name == "Pegasos") {
- (model as Pegasos).update(trainingData.first, trainingData.second.map { it.toDouble() }.toTypedArray())
- } else if (name == "Adaline") {
- (model as Adaline).update(trainingData.first, trainingData.second.map { it.toDouble() }.toTypedArray())
- }
- }
- storeModelLocally(model)
- Log.i("Recommend", "Model completely loaded")
- return model
- }
-
- /**
- * get playcounts for local songs (i.e. get labels for local data)
- *
- * @return array of playcounts
- */
- fun getPlaycounts(): Array {
- val songBlocks = musicStore.getBlocksWithType("publish_release")
- val playcounts = Array(globalSongCount()) { 0.0 }
- for ((i, block) in songBlocks.withIndex()) {
- if (block.transaction["title"] != null && block.transaction["artist"] != null) {
- playcounts[i] = database.dbFeaturesQueries.getFeature(
- "local-${block.transaction["title"]}-${block.transaction["artist"]}"
- )
- .executeAsOneOrNull()?.count?.toDouble() ?: 0.0
- }
- }
- return playcounts
- }
-
- /**
- * extract song ids from trustchain blocks with music data
- *
- * @return distinct song ids
- */
- fun getSongIds(): Set {
- val songBlocks = musicStore.getBlocksWithType("publish_release")
- val songIds = HashSet()
- for (block in songBlocks) {
- if (block.transaction["title"] != null && block.transaction["artist"] != null) {
- songIds.add("${block.transaction["title"]}-${block.transaction["artist"]}")
- }
- }
- return songIds
- }
-
- /**
- * Update local features with features from new local file
- *
- * @param file unseen music file
- */
- fun updateLocalFeatures(file: File) {
- val mp3File = Mp3File(file)
- val k = if (mp3File.id3v2Tag != null)
- "local-${mp3File.id3v2Tag.title}-${mp3File.id3v2Tag.artist}"
- else if (mp3File.id3v1Tag != null)
- "local-${mp3File.id3v1Tag.title}-${mp3File.id3v1Tag.artist}"
- else
- return
- val existingFeature = database.dbFeaturesQueries.getFeature(key = k).executeAsOneOrNull()
- var count = 1
- if (existingFeature != null) {
- count = existingFeature.count.toInt() + 1
- Log.i("Recommend", "Song exists! Increment counter")
- }
- val mp3Features = extractMP3Features(mp3File)
- database.dbFeaturesQueries.addFeature(
- key = k,
- songFeatures = mp3Features.contentToString(),
- count = count.toLong()
- )
- }
-
- /**
- * extracts music block metadata and transforms it to array of features
- * that can be directly fed into ml models
- *
- * @param block music block
- * @return array of features for given instance
- */
- private fun blockMetadata(block: TrustChainBlock): Array {
- var year = -1.0
- var genre = -1.0
- val k: String
-
- if (block.transaction["title"] != null && block.transaction["artist"] != null) {
- k = "local-${block.transaction["title"]}-${block.transaction["artist"]}"
- val unseenFeatures = this.database.dbUnseenFeaturesQueries.getFeature(k).executeAsOneOrNull()
- if (unseenFeatures != null) {
- return Json.decodeFromString(unseenFeatures.songFeatures!!).toTypedArray()
- }
- }
-
- if (block.transaction["date"] != null) {
- try {
- year = Integer.parseInt(block.transaction["date"] as String).toDouble()
- } catch (e: Exception) {
- }
-
- try {
- // Ectrat year from string format '01/02/2020'
- year = Integer.parseInt(
- (
- block.transaction["date"] as
- String
- ).split("/").toTypedArray()[-1]
- ).toDouble()
- } catch (e: Exception) {
- }
- }
-
- if (block.transaction["genre"] != null) {
- genre = block.transaction["genre"] as Double
- }
-
- return arrayOf(year, genre) + Array(223) { -1.0 }
- }
-
- /**
- * get amount of songs from trustchain for which we can extract features
- *
- * @return amount of test songs
- */
- fun globalSongCount(): Int {
- return getSongBlockMetadata(musicStore.getBlocksWithType("publish_release")).size
- }
-
- private fun getSongBlockMetadata(songsHistory: List): Array> {
- val features = Array(songsHistory.size) { Array(totalAmountFeatures) { 0.0 } }
- for (i in songsHistory.indices) {
- features[i] = blockMetadata(songsHistory[i])
- }
- return features
- }
-
- /**
- * process local files adnd add songs features to the db
- */
- fun addAllLocalFeatures() {
- if (!musicDir.isDirectory) return
-
- val allFiles = musicDir.listFiles() ?: return
- Log.i("Recommend", "Amount of files is ${allFiles.size}")
-
- var idx = 0
- for (albumFile in allFiles) {
- Log.i("Recommend", "Local album is ${albumFile.name}")
- if (albumFile.isDirectory) {
- val audioFiles = albumFile.listFiles(AudioFileFilter()) ?: continue
- Log.i("Recommend", "Local songs amount in alum: ${audioFiles.size}")
- for (f in audioFiles) {
- if (Mp3File(f).id3v2Tag != null) {
- val updatedFile = Mp3File(f)
-
- val k = "local-${updatedFile.id3v2Tag.title}-${updatedFile.id3v2Tag.artist}"
- Log.i("Recommend", "${updatedFile.filename} haveFeature: ${haveFeature(k, zerosFine = false)}")
- if (!haveFeature(k, zerosFine = false)) {
- try {
- val mp3Features = extractMP3Features(Mp3File(f))
- val count = 1
- database.dbFeaturesQueries.addFeature(
- key = k,
- songFeatures = mp3Features.contentToString(),
- count = count.toLong()
- )
- } catch (e: Exception) {
- Log.e("Recommend", "Extracting audio features failed!")
- Log.e("Recommend", e.toString())
- }
- }
- idx += 1
- }
- }
- }
- }
- }
-
- /**
- * this method is used upon reception of gossiped features (by other peers)
- * process received features and store to the table of unseen features
- * if don't have this song locally
- *
- * @param songIdentifier song id
- * @param features song feature
- */
- fun addNewFeatures(songIdentifier: String, features: String) {
- val seen = database.dbFeaturesQueries.getFeature(songIdentifier).executeAsOneOrNull()
- val unseen = database.dbUnseenFeaturesQueries.getFeature(songIdentifier).executeAsOneOrNull()
- if (seen == null && unseen == null) {
- database.dbUnseenFeaturesQueries.addFeature(
- key = songIdentifier,
- songFeatures = features
- )
- }
- }
-
- /**
- * get local song data for model training
- *
- * @return training data (pair of features and labels) from local songs
- */
- fun getLocalSongData(): Pair>, IntArray> {
- if (!essentiaJob.isActive)
- essentiaJob = GlobalScope.launch { addAllLocalFeatures() } // analyze local music files
- val batch = database.dbFeaturesQueries.getAllFeatures().executeAsList()
- if (batch.isEmpty()) {
- Log.w(
- "Recommend",
- "Local feature database is empty! " +
- "Analyzing files in background thread now, current recommendation will be empty."
- )
- return Pair(emptyArray(), emptyArray().toIntArray())
- }
- val features = Array(batch.size) { Array(totalAmountFeatures) { 0.0 } }
- val playcounts = Array(batch.size) { 0 }.toIntArray()
- for (i in batch.indices) {
- features[i] = Json.decodeFromString(batch[i].songFeatures!!).toTypedArray()
- playcounts[i] = batch[i].count.toInt()
- }
- return Pair(features, playcounts)
- }
-
- /**
- * load new songs from trustchain
- *
- * @return pair of new song's features and corresponding trustchain blocks
- */
- fun getNewSongs(): Pair>, List> {
- val songsHistory = musicStore.getBlocksWithType("publish_release")
- val data = getSongBlockMetadata(songsHistory)
- return Pair(data, songsHistory)
- }
-
- /**
- * extracts mp3features from a given file using Essentia
- *
- * @param mp3File file with song
- * @return features
- */
- private fun extractMP3Features(mp3File: Mp3File): Array {
- var features = Array(225) { -1.0 }
- val year = (mp3File.id3v2Tag ?: mp3File.id3v1Tag)?.year?.toDouble() ?: -1.0
- val genre = (mp3File.id3v2Tag ?: mp3File.id3v1Tag)?.genre?.toDouble() ?: -1.0
- try {
- val filename = mp3File.filename
-
- val k = if (mp3File.id3v2Tag != null)
- "local-${mp3File.id3v2Tag.title}-${mp3File.id3v2Tag.artist}"
- else if (mp3File.id3v1Tag != null)
- "local-${mp3File.id3v1Tag.title}-${mp3File.id3v1Tag.artist}"
- else
- ""
-
- if (haveFeature(k)) {
- val featureText = database.dbFeaturesQueries.getFeature(k).executeAsOneOrNull()
- ?.songFeatures
- ?: database.dbUnseenFeaturesQueries.getFeature(k).executeAsOneOrNull()?.songFeatures
- return Json.decodeFromString(featureText!!).toTypedArray()
- }
-
- val jsonfile = filename.replace(".mp3", ".json")
-
- if (!File(jsonfile).exists()) {
- if (Essentia.extractData(filename, jsonfile) == 1) {
- Log.e("Recommend", "Error extracting data with Essentia")
- } else {
- Log.i("Recommend", "Got essentia features for $filename")
- }
- }
- val essentiaFeatures = JSONObject(File(jsonfile).bufferedReader().use { it.readText() })
- val keys = arrayOf(
- "barkbands_crest", "barkbands_flatness_db", "barkbands_kurtosis", "barkbands_skewness", "barkbands_spread",
- "dissonance", "erbbands_crest", "erbbands_flatness_db", "erbbands_kurtosis", "erbbands_skewness",
- "erbbands_spread", "hfc", "melbands_crest", "melbands_flatness_db", "melbands_kurtosis", "melbands_skewness",
- "melbands_spread", "pitch_salience", "spectral_centroid", "spectral_complexity", "spectral_decrease",
- "spectral_energy", "spectral_energyband_high", "spectral_energyband_low", "spectral_energyband_middle_high",
- "spectral_energyband_middle_low", "spectral_entropy", "spectral_flux", "spectral_kurtosis", "spectral_rms",
- "spectral_rolloff", "spectral_skewness", "spectral_spread", "spectral_strongpeak", "zerocrossingrate"
- )
-
- var dynamic_complexity = 0.0
- var average_loudness = 0.0
- var integrated_loudness = 0.0
- var loudness_range = 0.0
- var momentary = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- var short_term = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- val lowlevelStats = Array(keys.size) { Array(5) { -1.0 } }
-
- if (essentiaFeatures.has("lowlevel")) {
- val lowlevel = essentiaFeatures.getJSONObject("lowlevel")
- try {
- dynamic_complexity = lowlevel.getDouble("dynamic_complexity")
- average_loudness = lowlevel.getDouble("average_loudness")
- integrated_loudness = lowlevel.getJSONObject("loudness_ebu128").getDouble("integrated")
- loudness_range = lowlevel.getJSONObject("loudness_ebu128").getDouble("loudness_range")
- momentary = stats(lowlevel.getJSONObject("loudness_ebu128").getJSONObject("momentary"))
- short_term = stats(lowlevel.getJSONObject("loudness_ebu128").getJSONObject("short_term"))
-
- for ((i, key) in keys.withIndex()) {
- lowlevelStats[i] = stats(lowlevel.getJSONObject(key))
- }
- } catch (e: java.lang.Exception) {
- Log.e("Recommend", e.toString())
- }
- }
-
- val tonal: JSONObject
- var key = arrayOf(0.0, 0.0)
- var key_edma = arrayOf(0.0, 0.0)
- var key_krumhansl = arrayOf(0.0, 0.0)
- var key_temperley = arrayOf(0.0, 0.0)
- var chords_strength = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- var hpcp_crest = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- var hpcp_entropy = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- var tuning_nontempered_energy_ratio = 0.0
- var tuning_diatonic_strength = 0.0
- if (essentiaFeatures.has("tonal")) {
- tonal = essentiaFeatures.getJSONObject("tonal")
- try {
- key = scale2label(tonal.getString("chords_key"), tonal.getString("chords_scale"))
- key_edma = scale2label(
- tonal.getJSONObject("key_edma").getString("key"),
- tonal.getJSONObject("key_edma").getString("scale")
- )
- key_krumhansl = scale2label(
- tonal.getJSONObject("key_krumhansl").getString("key"),
- tonal.getJSONObject("key_krumhansl").getString("scale")
- )
- key_temperley = scale2label(
- tonal.getJSONObject("key_temperley").getString("key"),
- tonal.getJSONObject("key_temperley").getString("scale")
- )
- chords_strength = stats(tonal.getJSONObject("chords_strength"))
- hpcp_crest = stats(tonal.getJSONObject("hpcp_crest"))
- hpcp_entropy = stats(tonal.getJSONObject("hpcp_entropy"))
- tuning_nontempered_energy_ratio = tonal.getDouble("tuning_nontempered_energy_ratio")
- tuning_diatonic_strength = tonal.getDouble("tuning_diatonic_strength")
- } catch (e: java.lang.Exception) {
- Log.e("Recommend", e.toString())
- }
- }
-
- val rhythm: JSONObject
- var bpm = 0.0
- var danceability = 0.0
- var beats_loudness = arrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
- if (essentiaFeatures.has("rhythm")) {
- try {
- rhythm = essentiaFeatures.getJSONObject("rhythm")
- bpm = rhythm.getDouble("bpm")
- danceability = rhythm.getDouble("danceability")
- beats_loudness = stats(rhythm.getJSONObject("beats_loudness"))
- } catch (e: java.lang.Exception) {
- Log.e("Recommend", e.toString())
- }
- }
-
- val metadata: JSONObject
- var length = 0.0
- var replay_gain = 0.0
- if (essentiaFeatures.has("rhythm")) {
- metadata = essentiaFeatures.getJSONObject("metadata")
- try {
- length = metadata.getJSONObject("audio_properties").getDouble("length")
- replay_gain = metadata.getJSONObject("audio_properties").getDouble("replay_gain")
- } catch (e: java.lang.Exception) {
- Log.e("Recommend", e.toString())
- }
- }
-
- features = arrayOf(
- arrayOf(
- year, genre, length, replay_gain, dynamic_complexity, average_loudness,
- integrated_loudness, loudness_range, bpm, danceability, tuning_nontempered_energy_ratio,
- tuning_diatonic_strength
- ),
- momentary, short_term, lowlevelStats.flatten().toTypedArray(), key, key_edma, key_krumhansl, key_temperley,
- chords_strength, hpcp_crest, hpcp_entropy, beats_loudness
- ).flatten().toTypedArray()
- } catch (e: Exception) {
- Log.e("Recommend", "Essentia extraction failed:")
- Log.e("Recommend", e.stackTraceToString())
- }
-
- // log transform on features
- for ((i, feat) in features.withIndex()) {
- features[i] = if (feat < 0.0) -log10(-feat) else if (feat > 0.0) log10(feat) else 0.0
- }
- Log.i("Recommend", "Extracted MP3 features")
-
- /**
- * pick most 'promising' Essentia features, read docs for more insight
- * we still keep 2 block features - year and genre,
- * in order to have some data for completely unseen features
- */
- val finalFeatures = Array(totalAmountFeatures) { 0.0 }
- finalFeatures[0] = year
- finalFeatures[1] = genre
- for ((idx, fidx) in this.highVarianceFeatureLabels.withIndex()) {
- finalFeatures[idx] = features[fidx + 1]
- }
-
- return finalFeatures
- }
-
- fun getMyFeatures(): List {
- return this.database.dbFeaturesQueries.getAllFeatures().executeAsList()
- }
-
- private fun getRemoteFeatures(): List {
- return this.database.dbUnseenFeaturesQueries.getAllFeatures().executeAsList()
- }
-
- private fun haveFeature(key: String, zerosFine: Boolean = true): Boolean {
- for (feat in getMyFeatures())
- if (feat.key == key) {
- return if (zerosFine) true else {
- for ((i, d) in Json.decodeFromString(feat.songFeatures!!).toTypedArray().withIndex()) {
- if (i >= 2 && d != 0.0)
- return true
- }
- return false
- }
- }
- for (feat in getRemoteFeatures())
- if (feat.key == key) {
- return if (zerosFine) true else {
- for ((i, d) in Json.decodeFromString(feat.songFeatures!!).toTypedArray().withIndex()) {
- if (i >= 2 && d != 0.0)
- return true
- }
- return false
- }
- }
- return false
- }
-
- companion object {
- @SuppressLint("StaticFieldLeak")
- private lateinit var instance: RecommenderStore
- fun getInstance(musicStore: TrustChainSQLiteStore, database: Database): RecommenderStore {
- if (!Companion::instance.isInitialized) {
- instance = RecommenderStore(musicStore, database)
- }
- return instance
- }
- }
-}
-
-// positions of scales on the circle of fifths
-val majorKeys = mapOf(
- "C" to 0.0, "G" to 1.0, "D" to 2.0, "A" to 3.0, "E" to 4.0, "B" to 5.0, "Gb" to 6.0,
- "F#" to 6.0, "Db" to 7.0, "Ab" to 8.0, "Eb" to 9.0, "Bb" to 10.0, "F" to 11.0
-)
-val minorKeys = mapOf(
- "A" to 0.0, "E" to 1.0, "B" to 2.0, "F#" to 3.0, "C#" to 4.0, "G#" to 5.0, "D#" to 6.0,
- "Eb" to 6.0, "Bb" to 7.0, "F" to 8.0, "C" to 9.0, "G" to 10.0, "D" to 11.0
-)
-fun scale2label(key: String, mode: String): Array {
- val keyCode = (if (mode == "major") majorKeys[key] else minorKeys[key]) ?: -1.0
- val modeCode = if (mode == "major") 0.0 else 1.0
- return arrayOf(keyCode, modeCode)
-}
-fun stats(obj: JSONObject): Array {
- return arrayOf(
- obj.getDouble("min"),
- obj.getDouble("median"),
- obj.getDouble("mean"),
- obj.getDouble("max"),
- obj.getDouble("var"),
- )
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/FeaturesExchangeMessage.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/FeaturesExchangeMessage.kt
deleted file mode 100644
index d08827601..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/FeaturesExchangeMessage.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package nl.tudelft.trustchain.gossipML.ipv8
-
-import nl.tudelft.ipv8.messaging.*
-
-/**
- * This is a message from a peer sending local songs features to other peers
- */
-open class FeaturesExchangeMessage @ExperimentalUnsignedTypes constructor(
- val originPublicKey: ByteArray,
- var ttl: UInt,
- val songIdentifier: String,
- val features: String
-) : Serializable {
-
- override fun serialize(): ByteArray {
- return originPublicKey +
- serializeUInt(ttl) +
- serializeVarLen(songIdentifier.toByteArray(Charsets.UTF_8)) +
- serializeVarLen(features.toByteArray(Charsets.UTF_8))
- }
-
- @kotlin.ExperimentalUnsignedTypes
- fun checkTTL(): Boolean {
- ttl -= 1u
- if (ttl < 1u) return false
- return true
- }
-
- companion object Deserializer : Deserializable {
- @ExperimentalUnsignedTypes
- @JvmStatic
- override fun deserialize(buffer: ByteArray, offset: Int): Pair {
- var localOffset = 0
- val originPublicKey = buffer.copyOfRange(
- offset + localOffset,
- offset + localOffset + SERIALIZED_PUBLIC_KEY_SIZE
- )
-
- localOffset += SERIALIZED_PUBLIC_KEY_SIZE
- val ttl = deserializeUInt(buffer, offset + localOffset)
- localOffset += SERIALIZED_UINT_SIZE
-
- val (songIdentifierBytes, songIdentifierSize) = deserializeVarLen(buffer, offset + localOffset)
- val songIdentifier = songIdentifierBytes.toString(Charsets.UTF_8)
- localOffset += songIdentifierSize
- val (featuresBytes, featuresSize) = deserializeVarLen(buffer, offset + localOffset)
- val features = featuresBytes.toString(Charsets.UTF_8)
- localOffset += featuresSize
-
- return Pair(
- first = FeaturesExchangeMessage(
- originPublicKey = originPublicKey,
- ttl = ttl,
- songIdentifier = songIdentifier,
- features = features
- ),
- second = localOffset
- )
- }
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/ModelExchangeMessage.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/ModelExchangeMessage.kt
deleted file mode 100644
index d44fd4b74..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/ModelExchangeMessage.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package nl.tudelft.trustchain.gossipML.ipv8
-
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-import nl.tudelft.ipv8.messaging.*
-import nl.tudelft.trustchain.gossipML.models.Model
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.PublicMatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.feature_based.Adaline
-import nl.tudelft.trustchain.gossipML.models.feature_based.Pegasos
-
-/**
- * This is a message from a peer sending walking model to other peers
- */
-open class ModelExchangeMessage @ExperimentalUnsignedTypes constructor(
- val originPublicKey: ByteArray,
- var ttl: UInt,
- val modelType: String,
- val model: Model
-) : Serializable {
-
- override fun serialize(): ByteArray {
- return originPublicKey +
- serializeUInt(ttl) +
- serializeVarLen(modelType.toByteArray(Charsets.UTF_8)) +
- serializeVarLen(model.serialize().toByteArray(Charsets.UTF_8))
- }
-
- @kotlin.ExperimentalUnsignedTypes
- fun checkTTL(): Boolean {
- ttl -= 1u
- if (ttl < 1u) return false
- return true
- }
-
- companion object Deserializer : Deserializable {
- @ExperimentalUnsignedTypes
- @JvmStatic
- override fun deserialize(buffer: ByteArray, offset: Int): Pair {
- var localOffset = 0
- val originPublicKey = buffer.copyOfRange(
- offset + localOffset,
- offset + localOffset + SERIALIZED_PUBLIC_KEY_SIZE
- )
-
- localOffset += SERIALIZED_PUBLIC_KEY_SIZE
- val ttl = deserializeUInt(buffer, offset + localOffset)
- localOffset += SERIALIZED_UINT_SIZE
-
- val (modelTypeBytes, modelTypeSize) = deserializeVarLen(buffer, offset + localOffset)
- val modelType = modelTypeBytes.toString(Charsets.UTF_8)
- localOffset += modelTypeSize
- val (modelBytes, modelSize) = deserializeVarLen(buffer, offset + localOffset)
- val modelJsonStr = modelBytes.toString(Charsets.UTF_8)
- localOffset += modelSize
-
- val model = if (modelType == "Adaline")
- Json.decodeFromString(modelJsonStr)
- else if (modelType == "Pegasos")
- Json.decodeFromString(modelJsonStr)
- else
- Json.decodeFromString(modelJsonStr)
-
- return Pair(
- first = ModelExchangeMessage(
- originPublicKey = originPublicKey,
- ttl = ttl,
- modelType = modelType,
- model = model,
- ),
- second = localOffset
- )
- }
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/RequestModelMessage.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/RequestModelMessage.kt
deleted file mode 100644
index 39e782e48..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/RequestModelMessage.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package nl.tudelft.trustchain.gossipML.ipv8
-
-import nl.tudelft.ipv8.messaging.*
-
-/**
- * This is a message from a peer sending and asking for a model from other peers
- */
-open class RequestModelMessage @ExperimentalUnsignedTypes constructor(
- val originPublicKey: ByteArray,
- var ttl: UInt,
- val modelType: String
-) : Serializable {
-
- override fun serialize(): ByteArray {
- return originPublicKey + serializeUInt(ttl)
- }
-
- @kotlin.ExperimentalUnsignedTypes
- fun checkTTL(): Boolean {
- ttl -= 1u
- if (ttl < 1u) return false
- return true
- }
-
- companion object Deserializer : Deserializable {
- @ExperimentalUnsignedTypes
- @JvmStatic
- override fun deserialize(buffer: ByteArray, offset: Int): Pair {
- var localOffset = 0
-
- val originPublicKey = buffer.copyOfRange(
- offset + localOffset,
- offset + localOffset + SERIALIZED_PUBLIC_KEY_SIZE
- )
- localOffset += SERIALIZED_PUBLIC_KEY_SIZE
-
- val ttl = deserializeUInt(buffer, offset + localOffset)
- localOffset += SERIALIZED_UINT_SIZE
-
- val (modelTypeBytes, modelTypeSize) = deserializeVarLen(buffer, offset + localOffset)
- val modelType = modelTypeBytes.toString(Charsets.UTF_8)
- localOffset += modelTypeSize
-
- return Pair(
- first = RequestModelMessage(
- originPublicKey = originPublicKey,
- ttl = ttl,
- modelType = modelType
- ),
- second = localOffset
- )
- }
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/SerializableSparseArray.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/SerializableSparseArray.kt
deleted file mode 100644
index b9a4e6b49..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/ipv8/SerializableSparseArray.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package nl.tudelft.trustchain.gossipML.ipv8
-
-import android.util.SparseArray
-import kotlinx.serialization.*
-import java.io.IOException
-import java.io.ObjectInputStream
-import java.io.ObjectOutputStream
-
-/**
- * @author Asaf Pinhassi www.mobiledev.co.il https://stackoverflow.com/a/21574953
- * @param
- */
-@Serializable
-class SerializableSparseArray : SparseArray {
- constructor(capacity: Int) : super(capacity) {}
- constructor() : super() {}
-
- /**
- * This method is private but it is called using reflection by java
- * serialization mechanism. It overwrites the default object serialization.
- *
- * **IMPORTANT**
- * The access modifier for this method MUST be set to **private** otherwise [java.io.StreamCorruptedException]
- * will be thrown.
- *
- * @param oos
- * the stream the data is stored into
- * @throws IOException
- * an exception that might occur during data storing
- */
- @Throws(IOException::class)
- private fun writeObject(oos: ObjectOutputStream) {
- val data = arrayOfNulls(size())
- for (i in data.indices.reversed()) {
- val pair = arrayOf(keyAt(i), valueAt(i))
- data[i] = pair
- }
- oos.writeObject(data)
- }
-
- /**
- * This method is private but it is called using reflection by java
- * serialization mechanism. It overwrites the default object serialization.
- *
- *
**IMPORTANT**
- * The access modifier for this method MUST be set to **private** otherwise [java.io.StreamCorruptedException]
- * will be thrown.
- *
- * @param oos
- * the stream the data is read from
- * @throws IOException
- * an exception that might occur during data reading
- * @throws ClassNotFoundException
- * this exception will be raised when a class is read that is
- * not known to the current ClassLoader
- * @throws ClassCastException
- * thrown when cast to E does not succeed
- */
- @Throws(IOException::class, ClassNotFoundException::class, ClassCastException::class)
- private fun readObject(ois: ObjectInputStream) {
- val data = ois.readObject() as Array<*>
- for (i in data.indices.reversed()) {
- val pair = data[i] as Array<*>
- val key = pair[0] as Int
- @Suppress("UNCHECKED_CAST") val value = pair[1] as E
- this.append(key, value)
- }
- return
- }
-
- companion object {
- private const val serialVersionUID = 824056059663678000L
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/DietNumpy.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/DietNumpy.kt
deleted file mode 100644
index 3a8809c45..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/DietNumpy.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models
-
-operator fun Array.plus(other: Double): Array {
- return this.map { it + other }.toTypedArray()
-}
-
-operator fun Double.plus(doubles: Array): Array {
- return doubles + this
-}
-
-operator fun Array.minus(other: Double): Array {
- return this.map { it - other }.toTypedArray()
-}
-
-operator fun Double.minus(doubles: Array): Array {
- return doubles - this
-}
-
-operator fun Array.times(other: Double): Array {
- return this.map { it * other }.toTypedArray()
-}
-
-operator fun Double.times(doubles: Array): Array {
- return doubles * this
-}
-
-operator fun Array.div(other: Double): Array {
- return this.map { it / other }.toTypedArray()
-}
-
-operator fun Double.div(doubles: Array): Array {
- return doubles / this
-}
-
-operator fun Array.plus(other: Array): Array {
- return this.zip(other).map { (i1, i2) -> i1 + i2 }.toTypedArray()
-}
-
-operator fun Array.minus(other: Array): Array {
- return this.zip(other).map { (i1, i2) -> i1 - i2 }.toTypedArray()
-}
-
-operator fun Array.times(other: Array): Double {
- return this.zip(other).map { (i1, i2) -> i1 * i2 }.toTypedArray().sum()
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/Model.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/Model.kt
deleted file mode 100644
index c7df97548..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/Model.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models
-
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-
-/**
- * General model class to accommodate both feature-based and
- * collaborative filtering models
- *
- * @property name model name
- */
-@Serializable
-open class Model(open val name: String) {
- open fun serialize(): String {
- return Json.encodeToString(this)
- }
-
- open fun update() {}
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/OnlineModel.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/OnlineModel.kt
deleted file mode 100644
index e6ed5717c..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/OnlineModel.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models
-import kotlinx.serialization.*
-import kotlinx.serialization.json.Json
-import java.util.*
-
-/**
- * Online model generalization of Adaline and Pegasos models (feature-based models)
- */
-@Serializable
-open class OnlineModel : Model {
- val amountFeatures: Int
- var weights: Array
-
- constructor(amountFeatures: Int, name: String) : super(name) {
- this.amountFeatures = amountFeatures
- this.weights = Array(amountFeatures) { _ -> 1.0 }
- }
-
- /**
- * merge 2 online models
- *
- * @param otherOnlineModel other model to merge with
- * @return merged model
- */
- fun merge(otherOnlineModel: OnlineModel): OnlineModel {
- for (idx in weights.indices) {
- weights[idx] = (weights[idx] * 0.9) + (otherOnlineModel.weights[idx] / 10) // it was divied by 2?
- }
- return this
- }
-
- /**
- * make predictions for a batch of input instances
- *
- * @param x - array of feature arrays
- * @param y - array of labels
- * @return array of predictions
- */
- fun predict(x: Array>): Array {
- val result = Array(x.size) { 0.0 }
- for ((idx, item) in x.withIndex()) {
- result[idx] = predict(item)
- }
- return result
- }
-
- /**
- * test method for getting prediction accuracy on some test instances
- *
- * @param x - array of feature arrays
- * @param y - array of labels
- * @return prediction score
- */
- fun score(x: Array>, y: Array): Double {
- var correct = 0.0
- for (i in x.indices) if (predict(x[i]) == y[i]) correct ++
- return (correct / x.size)
- }
-
- open fun update(x: Array>, y: Array) {}
-
- open fun predict(x: Array): Double {
- return 1.0
- }
-
- open fun update(x: Array, y: Double) {}
-
- override fun serialize(): String {
- return Json.encodeToString(this)
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/collaborative_filtering/MatrixFactorization.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/collaborative_filtering/MatrixFactorization.kt
deleted file mode 100644
index faf592e4f..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/collaborative_filtering/MatrixFactorization.kt
+++ /dev/null
@@ -1,280 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models.collaborative_filtering
-
-import android.util.Log
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.builtins.MapSerializer
-import kotlinx.serialization.builtins.serializer
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.Json
-import nl.tudelft.trustchain.gossipML.models.Model
-import nl.tudelft.trustchain.gossipML.models.plus
-import nl.tudelft.trustchain.gossipML.models.times
-import java.lang.Double.NEGATIVE_INFINITY
-import java.util.*
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.sqrt
-import kotlin.random.Random.Default.nextDouble
-
-object SongFeatureSerializer : KSerializer> {
- private val mapSerializer = MapSerializer(String.serializer(), SongFeature.serializer())
- override val descriptor: SerialDescriptor = mapSerializer.descriptor
- override fun serialize(encoder: Encoder, value: SortedMap) {
- mapSerializer.serialize(encoder, value)
- }
- override fun deserialize(decoder: Decoder): SortedMap {
- return mapSerializer.deserialize(decoder).toSortedMap()
- }
-}
-
-object RatingSerializer : KSerializer> {
- private val mapSerializer = MapSerializer(String.serializer(), Double.serializer())
- override val descriptor: SerialDescriptor = mapSerializer.descriptor
- override fun serialize(encoder: Encoder, value: SortedMap) {
- mapSerializer.serialize(encoder, value)
- }
- override fun deserialize(decoder: Decoder): SortedMap {
- return mapSerializer.deserialize(decoder).toSortedMap()
- }
-}
-
-/**
- * Publicly avaliable part of matrix factarization model
- *
- * @property peerFeatures amount of peer features
- */
-@Serializable
-data class PublicMatrixFactorization(
- @Serializable(with = SongFeatureSerializer::class)
- val peerFeatures: SortedMap
-) : Model("PublicMatrixFactorization")
-
-/**
- * Class that represents song features for matrix factorization model
- *
- * @property age song age
- * @property feature normal song features
- * @property bias song bias
- */
-@Serializable
-data class SongFeature(
- var age: Double,
- var feature: Array,
- var bias: Double
-) {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
- other as SongFeature
- if (age != other.age) return false
- if (!feature.contentEquals(other.feature)) return false
- if (bias != other.bias) return false
- return true
- }
-
- override fun hashCode(): Int {
- var result = age.hashCode()
- result = 31 * result + feature.contentHashCode()
- result = 31 * result + bias.hashCode()
- return result
- }
-}
-
-fun songFeaturesFromArrays(age: Array, features: Array>, bias: Array): Array {
- return age.zip(features).zip(bias) { (a, b), c -> SongFeature(a, b, c) }.toTypedArray()
-}
-
-/**
- * General matrix factorization model
- *
- * @property ratings
- */
-@Serializable
-open class MatrixFactorization(
- @Serializable(with = RatingSerializer::class) var ratings: SortedMap
-) : Model("MatrixFactorization") {
- val k = 5
-
- private val lr = 0.01
- private val lambda = 1
-
- private val minR = 0.0
- private var maxR = 20.0
-
- @Serializable(with = SongFeatureSerializer::class)
- var songFeatures = ratings.keys.zip(
- songFeaturesFromArrays(
- Array(ratings.size) { i -> if (ratings[ratings.keys.toTypedArray()[i]]!! > 0.0) 1.0 else 0.0 }, // ages
- Array(ratings.size) { Array(k) { initFeat() } }, // song features
- Array(ratings.size) { 0.0 } // biases
- )
- ).toMap().toSortedMap()
-
- private var rateFeature = Array(k) { initFeat() }
- private var rateBias = 0.0
-
- constructor(peerModel: PublicMatrixFactorization) : this(peerModel.peerFeatures)
- constructor(peerFeatures: SortedMap, True: Boolean = true) : this(
- ratings = peerFeatures.keys.zip(Array(peerFeatures.size) { 0.0 }).toMap().toSortedMap()
- ) {
- // True in constructor to avoid java type signature class with main constructor
- if (True) this.songFeatures = peerFeatures.toSortedMap()
- }
-
- /**
- * Initalize feature
- *
- * @return initialized feature
- */
- private fun initFeat(): Double {
- return nextDouble() * sqrt((maxR - minR) / k)
- }
-
- /**
- * Predict most fitting song
- *
- * @return name of most relevant song
- */
- fun predict(): String {
- var bestSong = ""
- var mostRelevant = NEGATIVE_INFINITY
- songFeatures.forEach {
- val (name, triple) = it
- val (_, feature, bias) = triple
- if (ratings[name]!! == 0.0) {
- val relevance = rateFeature * feature + rateBias + bias
- if (relevance > mostRelevant) {
- bestSong = name
- mostRelevant = relevance
- }
- }
- }
- Log.i("Recommend", "Best colaborative score: $mostRelevant")
- return bestSong
- }
-
- /**
- * Updates matrix ratings
- *
- * @param newRatings new ratings to be merged with old ones
- */
- fun updateRatings(newRatings: SortedMap) {
- for ((name, rating) in newRatings) {
- ratings[name] = rating
- if (songFeatures[name] == null) { // maybe initialize newly rated song
- songFeatures[name] = SongFeature(0.0, Array(k) { initFeat() }, 0.0)
- }
- }
- update()
- }
-
- /**
- * Update helper method to update songs features
- *
- */
- override fun update() {
- var errTotal = 0.0
- songFeatures.forEach {
- val (name, triple) = it
- val (age, feature, bias) = triple
- if (ratings[name] != 0.0) {
- val err = ratings[name]!! - rateFeature * feature - rateBias - bias
- errTotal += abs(err)
-
- val (newSongFeature, newRateFeature) = Pair(
- (1.0 - lr * lambda) * feature + lr * err * rateFeature,
- (1.0 - lr * lambda) * rateFeature + lr * err * feature
- )
-
- songFeatures[name]!!.age = age + 1.0
-
- songFeatures[name]!!.feature = newSongFeature
- rateFeature = newRateFeature
-
- songFeatures[name]!!.bias += lr * err
- rateBias += lr * err
- }
- }
- }
-
- /**
- * Open function to merge matrix fatorization models
- *
- * @param peerModel
- */
- open fun merge(peerModel: PublicMatrixFactorization) {
- merge(peerModel.peerFeatures)
- }
-
- /**
- * Actual merging function
- *
- * @param peerFeatures features to be merged with
- */
- open fun merge(peerFeatures: SortedMap) {
- if (peerFeatures.keys.toSet() != songFeatures.keys) {
- for (name in peerFeatures.keys.toSet() + songFeatures.keys.toSet()) {
- // initialize rows not yet present in each map
- if (songFeatures[name] == null) {
- songFeatures[name] = SongFeature(0.0, Array(k) { initFeat() }, 0.0)
- }
- if (ratings[name] == null) {
- ratings[name] = 0.0
- }
- if (peerFeatures[name] == null) {
- peerFeatures[name] = SongFeature(0.0, Array(k) { initFeat() }, 0.0)
- }
- }
- }
-
- // age weighted average, more weight to rows which have been updated many times
- songFeatures.forEach {
- val (name, triple) = it
- val (age, feature, bias) = triple
- val tripleNew = peerFeatures[name]!!
- val (ageNew, featureNew, biasNew) = tripleNew
- if (ageNew != 0.0) {
- val w = ageNew / (age + ageNew)
- songFeatures[name] = SongFeature(
- age = max(age, ageNew),
- feature = feature * (1 - w) + featureNew * w,
- bias = bias * (1 - w) + biasNew * w
- )
- }
- }
-
- update()
- }
-
- private fun compress(map: SortedMap): SortedMap {
- /*
- from paper: subsample songFeatures to a fixed number of rows instead of whole thing
- maybe also compress with lz4 or something?
- */
- return map
- }
-
- /**
- * the function used during automatic serialization in model exchange messages
- * don't serialize private ratings matrix
- */
- override fun serialize(): String {
- return this.serialize(private = false)
- }
-
- /**
- * the function used during serialization for local database storage
- */
- fun serialize(private: Boolean): String {
- Log.i(
- "Recommend",
- "Serializing MatrixFactorization, including private data: $private"
- )
- return if (private) Json.encodeToString(this)
- else Json.encodeToString(PublicMatrixFactorization(compress(songFeatures.toSortedMap())))
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Adaline.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Adaline.kt
deleted file mode 100644
index bee9c97d0..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Adaline.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models.feature_based
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import nl.tudelft.trustchain.gossipML.models.OnlineModel
-import java.util.*
-
-/**
- * Machine Learning Adaline model (Adaptive Linear Neuron)
- * Single layer NN used in gossiping user preference models
- */
-@Serializable
-class Adaline : OnlineModel {
- private val learningRate: Double
- var bias = Random().nextDouble()
-
- constructor(learningRate: Double, amountFeatures: Int) : super(amountFeatures, "Adaline") {
- this.learningRate = learningRate
- }
-
- /**
- * update model with single data instance
- *
- * @param x - feature array
- * @param y - label
- */
- override fun update(x: Array, y: Double) {
- val error = y - activation(forward(x))
- this.bias += this.learningRate * error
- for ((idx, item) in x.withIndex()) {
- weights[idx] = weights[idx] + learningRate * error * item
- }
- }
-
- /**
- * update model with multiple data instances
- *
- * @param x - array of feature arrays
- * @param y - array of labels
- */
- override fun update(x: Array>, y: Array) {
- require(x.size == y.size) {
- String.format("Input vector x of size %d not equal to length %d of y", x.size, y.size)
- }
- for (i in x.indices) {
- update(x[i], y[i])
- }
- }
-
- /**
- * predict score for a given data instance
- *
- * @param x feature array
- * @return predicted score
- */
- override fun predict(x: Array): Double {
- return activation(forward(x))
- }
-
- /**
- * binary classification of input instance based on activation
- * e.g. - like / dislike song
- *
- * @param x feature array
- * @return binary predicted label
- */
- fun classify(x: Array): Double {
- return if (activation(forward(x)) >= 0.0) {
- 1.0
- } else {
- 0.0
- }
- }
-
- /**
- * Adaline activation function
- *
- * @param x output of the previous neuron
- * @return activation of x
- */
- private fun activation(x: Double): Double {
- return x
- }
-
- /**
- * Forward pass of Adaline
- *
- * @param x single data instance
- * @return propagated value
- */
- private fun forward(x: Array): Double {
- var weightedSum = this.bias
- for (idx in 1 until x.size) {
- weightedSum += this.weights[idx] * x[idx]
- }
- return weightedSum
- }
-
- override fun serialize(): String {
- return Json.encodeToString(this)
- }
-}
diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Pegasos.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Pegasos.kt
deleted file mode 100644
index 0966bafc3..000000000
--- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/models/feature_based/Pegasos.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models.feature_based
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import nl.tudelft.trustchain.gossipML.models.OnlineModel
-import nl.tudelft.trustchain.gossipML.models.plus
-import nl.tudelft.trustchain.gossipML.models.times
-import java.util.*
-
-/**
- * Pegasos Machine Learning model (Primal Estimated sub-gradient solver for SVM)
- */
-@Serializable
-class Pegasos : OnlineModel {
- private val regularization: Double
- private val iterations: Int
-
- constructor(regularization: Double, amountFeatures: Int, iterations: Int) : super(amountFeatures, "Pegasos") {
- this.regularization = regularization
- this.iterations = iterations
- }
-
- /**
- * update model with single data instance
- *
- * @param x - feature array
- * @param y - label
- */
- override fun update(x: Array, y: Double) {
- val eta = 1.0 / regularization
- gradientSVM(x, y, eta)
- }
-
- /**
- * update model with multiple data instances
- *
- * @param x - array of feature arrays
- * @param y - array of labels
- */
- override fun update(x: Array>, y: Array) {
- require(x.size == y.size) {
- String.format("Input vector x of size %d not equal to length %d of y", x.size, y.size)
- }
-
- for (iteration in 0..iterations) {
- val i = Random().nextInt(x.size)
- val eta = 1.0 / (regularization * (i + 1))
-
- gradientSVM(x[i], y[i], eta)
- }
- }
-
- /**
- * Pegasos activation function used in predictions
- *
- * @param x output of SVM model
- * @return activation of x
- */
- private fun activation(x: Double): Double {
- return x
- }
-
- /**
- * predict score for a given data instance
- *
- * @param x feature array
- * @return predicted score
- */
- override fun predict(x: Array): Double {
- return activation(weights * x)
- }
-
- /**
- * binary classification of input instance based on activation
- * e.g. - like / dislike song
- *
- * @param x feature array
- * @return binary predicted label
- */
- fun classify(x: Array): Double {
- return if (activation(weights * x) >= 0.0) {
- 1.0
- } else {
- 0.0
- }
- }
-
- /**
- * learning function of Pegasos, updates model weights
- *
- * @param x - features
- * @param y - label
- * @param eta - step value
- */
- private fun gradientSVM(x: Array, y: Double, eta: Double) {
- val score = weights * x
- weights = if (y * score < 1.0) {
- (1.0 - eta * regularization) * weights + eta * y * x
- } else {
- (1.0 - eta * regularization) * weights
- }
- }
-
- override fun serialize(): String {
- return Json.encodeToString(this)
- }
-}
diff --git a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbFeatures.sq b/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbFeatures.sq
deleted file mode 100644
index 92bee512f..000000000
--- a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbFeatures.sq
+++ /dev/null
@@ -1,22 +0,0 @@
-CREATE TABLE features (
- key TEXT NOT NULL,
- songFeatures TEXT,
- count INTEGER NOT NULL,
- PRIMARY KEY (key)
-);
-
-addFeature:
-REPLACE INTO features (key, songFeatures, count)
-VALUES(?, ?, ?);
-
-getFeature:
-SELECT * FROM features WHERE key = ? LIMIT 1;
-
-getSongIds:
-SELECT key FROM features;
-
-getAllFeatures:
-SELECT * FROM features;
-
-deleteAllFeatures:
-DELETE FROM features;
diff --git a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbModel.sq b/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbModel.sq
deleted file mode 100644
index dacada5d7..000000000
--- a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbModel.sq
+++ /dev/null
@@ -1,19 +0,0 @@
-CREATE TABLE models (
- name TEXT NOT NULL,
- type TEXT NOT NULL,
- parameters TEXT NOT NULL,
- PRIMARY KEY (name)
-);
-
-addModel:
-REPLACE INTO models (name, type, parameters)
-VALUES(?, ?, ?);
-
-getModel:
-SELECT * FROM models WHERE name = ? LIMIT 1;
-
-deleteModel:
-DELETE FROM models WHERE name = ?;
-
-deleteAll:
-DELETE FROM models;
diff --git a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbUnseenFeatures.sq b/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbUnseenFeatures.sq
deleted file mode 100644
index c87570e51..000000000
--- a/gossipML/src/main/sqldelight/nl/tudelft/gossipML/sqldelight/DbUnseenFeatures.sq
+++ /dev/null
@@ -1,21 +0,0 @@
-CREATE TABLE unseen_features (
- key TEXT NOT NULL,
- songFeatures TEXT,
- PRIMARY KEY (key)
-);
-
-addFeature:
-REPLACE INTO unseen_features (key, songFeatures)
-VALUES(?, ?);
-
-getFeature:
-SELECT * FROM unseen_features WHERE key = ? LIMIT 1;
-
-getSongIds:
-SELECT key FROM unseen_features;
-
-getAllFeatures:
-SELECT * FROM unseen_features;
-
-deleteAllFeatures:
-DELETE FROM unseen_features;
diff --git a/gossipML/src/test/java/gossipML/ipv8/ModelExchangeMessageTest.kt b/gossipML/src/test/java/gossipML/ipv8/ModelExchangeMessageTest.kt
deleted file mode 100644
index f02e87107..000000000
--- a/gossipML/src/test/java/gossipML/ipv8/ModelExchangeMessageTest.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package nl.tudelft.trustchain.gossipML.ipv8
-
-import com.goterl.lazysodium.LazySodiumJava
-import com.goterl.lazysodium.SodiumJava
-import nl.tudelft.ipv8.keyvault.LibNaClSK
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.trustchain.gossipML.models.OnlineModel
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.MatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.feature_based.Adaline
-import nl.tudelft.trustchain.gossipML.models.feature_based.Pegasos
-import org.junit.Assert
-import org.junit.Test
-
-@ExperimentalUnsignedTypes
-class ModelExchangeMessageTest {
- private val lazySodium = LazySodiumJava(SodiumJava())
- private val key = LibNaClSK.fromBin(
- "4c69624e61434c534b3a054b2367b4854a8bf2d12fcd12158a6731fcad9cfbff7dd71f9985eb9f064c8118b1a89c931819d3482c73ebd9be9ee1750dc143981f7a481b10496c4e0ef982".hexToBytes(),
- lazySodium
- )
- private val originPublicKey = key.pub().keyToBin()
- private val ttl = 2u
- private val model1 = Pegasos(0.4, 10, 5)
- private val model2 = MatrixFactorization(Array(0) { "" }.zip(Array(0) { 0.0 }).toMap().toSortedMap())
-
- @ExperimentalUnsignedTypes
- @Test
- fun checkTTL() {
- val modelType = model1.name
- val payload = ModelExchangeMessage(originPublicKey, ttl, modelType, model1)
- Assert.assertTrue(payload.checkTTL())
- Assert.assertFalse(payload.checkTTL())
- }
-
- @ExperimentalUnsignedTypes
- @Test
- fun serializeAndDeserializeFeatureBasedModel() {
- val modelType = model1.name
- val payload = ModelExchangeMessage(originPublicKey, ttl, modelType, model1)
- val delta = 0.01
- val serialized = payload.serialize()
- val (deserialized, size) = ModelExchangeMessage.deserialize(serialized, 0)
- Assert.assertEquals(serialized.size, size)
- Assert.assertEquals(payload.modelType, deserialized.modelType)
- Assert.assertArrayEquals(
- (payload.model as OnlineModel).weights,
- (deserialized.model as OnlineModel).weights
- )
- if (payload.model is Adaline && deserialized.model is Adaline) {
- Assert.assertEquals(
- (payload.model as Adaline).bias,
- (deserialized.model as Adaline).bias,
- delta
- )
- }
-
- Assert.assertEquals(payload.ttl, deserialized.ttl)
- Assert.assertArrayEquals(payload.originPublicKey, deserialized.originPublicKey)
- }
-
- @ExperimentalUnsignedTypes
- @Test
- fun serializeAndDeserializeMFBasedModel() {
- val modelType = model2.name
- val payload = ModelExchangeMessage(originPublicKey, ttl, modelType, model2)
- val serialized = payload.serialize()
- val (deserialized, size) = ModelExchangeMessage.deserialize(serialized, 0)
- Assert.assertEquals(serialized.size, size)
- Assert.assertEquals(payload.modelType, deserialized.modelType)
-
- Assert.assertEquals(payload.ttl, deserialized.ttl)
- Assert.assertArrayEquals(payload.originPublicKey, deserialized.originPublicKey)
- }
-}
diff --git a/gossipML/src/test/java/gossipML/models/ColabFilterTest.kt b/gossipML/src/test/java/gossipML/models/ColabFilterTest.kt
deleted file mode 100644
index f829580dd..000000000
--- a/gossipML/src/test/java/gossipML/models/ColabFilterTest.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models
-
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.MatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.PublicMatrixFactorization
-import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.SongFeature
-import org.hamcrest.CoreMatchers.instanceOf
-import org.junit.Assert
-import org.junit.Test
-import kotlin.math.abs
-import kotlin.random.Random
-
-class ColabFilterTest {
-
- @Test
- fun testCreateAndUpdate() {
- val model = MatrixFactorization(Array(0) { "" }.zip(Array(0) { 0.0 }).toMap().toSortedMap())
-
- model.merge(
- sortedMapOf(
- Pair("a", SongFeature(5.0, Array(5) { Random.nextDouble(1.0, 5.0) }, 0.0)),
- Pair("b", SongFeature(2.0, Array(5) { Random.nextDouble(0.0, 3.0) }, 0.0)),
- Pair("c", SongFeature(7.0, Array(5) { Random.nextDouble(0.0, 8.0) }, 0.0)),
- )
- )
-
- Assert.assertThat(model, instanceOf(MatrixFactorization::class.java))
- Assert.assertEquals(model.songFeatures.size, 3)
- }
-
- @Test
- fun testPredictions() {
- val pubModel = PublicMatrixFactorization(
- sortedMapOf(
- Pair("good", SongFeature(1.0, Array(5) { Random.nextDouble(2.0, 5.0) }, 0.0)),
- Pair("bad", SongFeature(1.0, Array(5) { 1.0 }, 0.0)), // nobody likes this song
- )
- )
- val model = MatrixFactorization(pubModel)
- val pred = model.predict()
- Assert.assertEquals(pred, "good")
- }
-
- @Test
- fun testRatingsUpdate() {
- val model = MatrixFactorization(
- sortedMapOf(
- Pair("good", SongFeature(1.0, Array(5) { Random.nextDouble(2.0, 5.0) }, 0.0)),
- Pair("bad", SongFeature(1.0, Array(5) { 1.0 }, 0.0)), // nobody likes this song
- )
- )
-
- model.updateRatings(
- sortedMapOf(
- Pair("good", 0.0),
- Pair("bad", 1.0)
- )
- )
-
- Assert.assertNotEquals(model.songFeatures["bad"], Array(5) { 0.0 })
- }
-
- @Test
- fun testSyncModels() {
- val pubModel = PublicMatrixFactorization(
- sortedMapOf(
- Pair("good", SongFeature(1.0, Array(5) { Random.nextDouble(2.0, 5.0) }, 0.0)),
- Pair("bad", SongFeature(1.0, Array(5) { 1.0 }, 0.0)), // nobody likes this song
- )
- )
- val models = Array(3) { MatrixFactorization(pubModel) }
-
- val updatedModel = MatrixFactorization(pubModel)
- updatedModel.updateRatings(sortedMapOf(Pair("new", 5.0)))
-
- // gossip new song to peers
- for (model in models) {
- model.merge(PublicMatrixFactorization(updatedModel.songFeatures))
- Assert.assertTrue(model.songFeatures.keys.contains("new"))
- }
- }
-
- private fun pairwiseDifference(models: Array): Double {
- var diff = 0.0
- var total = 0.0
- for (m1 in models) {
- for (m2 in models) {
- if (m1 === m2) continue
- for (k in m1.songFeatures.keys) {
- diff += abs((m1.songFeatures[k]!!.feature - m2.songFeatures[k]!!.feature).sum())
- total += (m1.songFeatures[k]!!.feature + m2.songFeatures[k]!!.feature).sum() / 2
- }
- }
- }
- return diff / total
- }
-
- @Test
- fun testGossipConvergence() {
- val models = arrayOf(
- // a fans
- MatrixFactorization(sortedMapOf(Pair("a", 25.0), Pair("aa", 30.0), Pair("aaa", 27.0), Pair("b", 1.0), Pair("cc", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("a", 28.0), Pair("aa", 29.0), Pair("dd", 1.0), Pair("bbb", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("aaa", 21.0), Pair("aa", 23.0), Pair("c", 1.0), Pair("d", 1.0))),
- // b fans
- MatrixFactorization(sortedMapOf(Pair("b", 25.0), Pair("bb", 29.0), Pair("bbb", 24.0), Pair("a", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bb", 26.0), Pair("b", 28.0), Pair("d", 3.0), Pair("dd", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bbb", 27.0), Pair("b", 27.0), Pair("c", 2.0), Pair("aa", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bb", 28.0), Pair("bbb", 26.0), Pair("ccc", 1.0), Pair("aaa", 1.0))),
- // c fans
- MatrixFactorization(sortedMapOf(Pair("c", 21.0), Pair("cc", 26.0), Pair("ccc", 27.0), Pair("bbb", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("cc", 22.0), Pair("c", 25.0), Pair("bb", 1.0), Pair("d", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("ccc", 23.0), Pair("c", 24.0), Pair("b", 2.0), Pair("aa", 1.0), Pair("aaa", 1.0))),
- // d fan
- MatrixFactorization(sortedMapOf(Pair("d", 30.0), Pair("dd", 30.0), Pair("aaa", 1.0), Pair("bb", 1.0), Pair("c", 1.0))),
- )
-
- // gossip models iteratively until (hopefully) convergence
- for (round in 1..10) {
- for (m1 in models) {
- for (m2 in models) {
- if (m1 === m2) continue
- m2.merge(PublicMatrixFactorization(m1.songFeatures.toSortedMap()))
- }
- }
- }
-
- // models never converge completely because update() is called after merging
- Assert.assertTrue(pairwiseDifference(models) < 0.1)
- }
-
- @Test
- fun testRecommendations() {
- // high play counts within group, low play counts outside
- val models = arrayOf(
- // a fans
- MatrixFactorization(sortedMapOf(Pair("a", 25.0), Pair("aa", 30.0), Pair("aaa", 27.0), Pair("b", 1.0), Pair("cc", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("a", 28.0), Pair("aa", 29.0), Pair("dd", 1.0), Pair("bbb", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("aaa", 21.0), Pair("aa", 23.0), Pair("c", 1.0), Pair("d", 1.0))),
- // b fans
- MatrixFactorization(sortedMapOf(Pair("b", 25.0), Pair("bb", 29.0), Pair("bbb", 24.0), Pair("a", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bb", 26.0), Pair("b", 28.0), Pair("d", 3.0), Pair("dd", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bbb", 27.0), Pair("b", 27.0), Pair("c", 2.0), Pair("aa", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("bb", 28.0), Pair("bbb", 26.0), Pair("ccc", 1.0), Pair("aaa", 1.0))),
- // c fans
- MatrixFactorization(sortedMapOf(Pair("c", 21.0), Pair("cc", 26.0), Pair("ccc", 27.0), Pair("bbb", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("cc", 22.0), Pair("c", 25.0), Pair("bb", 1.0), Pair("d", 1.0))),
- MatrixFactorization(sortedMapOf(Pair("ccc", 23.0), Pair("c", 24.0), Pair("b", 2.0), Pair("aa", 1.0), Pair("aaa", 1.0))),
- // d fan
- MatrixFactorization(sortedMapOf(Pair("d", 30.0), Pair("dd", 30.0), Pair("aaa", 1.0), Pair("bb", 1.0), Pair("c", 1.0))),
- )
-
- // gossip models iteratively until convergence
- for (round in 1..30) {
- for (m1 in models) {
- for (m2 in models) {
- if (m1 === m2) continue
- m2.merge(PublicMatrixFactorization(m1.songFeatures.toSortedMap()))
- }
- }
- }
-
- // often there's still 1 error, but 87% is good enough for this test set
- val numCorrect = arrayOf(
- "aaa" == models[1].predict(),
- "a" == models[2].predict(),
- "bbb" == models[4].predict(),
- "bb" == models[5].predict(),
- "b" == models[6].predict(),
- "ccc" == models[8].predict(),
- "cc" == models[9].predict(),
- "b" == models[7].predict()
- ).map { if (it) 1 else 0 }.sum()
-
- Assert.assertTrue(numCorrect >= 5)
- }
-}
diff --git a/gossipML/src/test/java/gossipML/models/FeatureBasedTest.kt b/gossipML/src/test/java/gossipML/models/FeatureBasedTest.kt
deleted file mode 100644
index 6ed75ab67..000000000
--- a/gossipML/src/test/java/gossipML/models/FeatureBasedTest.kt
+++ /dev/null
@@ -1,354 +0,0 @@
-package nl.tudelft.trustchain.gossipML.models
-
-import nl.tudelft.trustchain.gossipML.db.scale2label
-import nl.tudelft.trustchain.gossipML.db.stats
-import nl.tudelft.trustchain.gossipML.models.feature_based.Adaline
-import nl.tudelft.trustchain.gossipML.models.feature_based.Pegasos
-import org.hamcrest.CoreMatchers.instanceOf
-import org.json.JSONObject
-import org.junit.Assert
-import org.junit.Test
-import java.io.File
-import kotlin.math.absoluteValue
-import kotlin.math.log10
-import kotlin.math.pow
-import kotlin.math.sqrt
-import kotlin.random.Random
-
-class FeatureBasedTest {
- private val amountFeatures = 10
- private var features: Array> = Array(amountFeatures) { _ -> Array(amountFeatures) { Random.nextDouble(0.0, 5.0) } }
- private var labels = Array(amountFeatures) { Random.nextInt(0, 2).toDouble() }
- private var highVarianceFeatureLabels = arrayOf(3, 62, 162, 223, 130, 140)
-
- @Test
- fun testPegasos() {
- val model = Pegasos(0.4, amountFeatures, 5)
-
- model.update(features, labels)
-
- model.predict(Array(amountFeatures) { Random.nextDouble(0.0, 5.0) })
-
- val mergeModelEq = Pegasos(0.4, amountFeatures, 5)
- model.merge(mergeModelEq)
- Assert.assertThat(model, instanceOf(Pegasos::class.java))
-
- val mergeModelDiff = Adaline(0.1, amountFeatures)
- model.merge(mergeModelDiff)
- Assert.assertThat(model, instanceOf(Pegasos::class.java))
- }
-
- @Test
- fun testAdaline() {
- val model = Adaline(0.1, amountFeatures)
-
- model.update(features, labels)
-
- model.predict(Array(amountFeatures) { Random.nextDouble(0.0, 5.0) })
-
- val mergeModelEq = Adaline(0.1, amountFeatures)
- model.merge(mergeModelEq)
- Assert.assertThat(model, instanceOf(Adaline::class.java))
-
- val mergeModelDiff = Pegasos(0.4, amountFeatures, 5)
- model.merge(mergeModelDiff)
- Assert.assertThat(model, instanceOf(Adaline::class.java))
- }
-
- @Test
- fun testAdalinePredictions() {
- val model = Adaline(1.0, 2)
- val biasedFeatures = arrayOf(arrayOf(100.0), arrayOf(-1.0))
- val biasedLabels = arrayOf(50.0, 0.0)
-
- for (i in 0..100) {
- model.update(biasedFeatures, biasedLabels)
- }
-
- val biasedTestSamples = arrayOf(arrayOf(100.0), arrayOf(-1.0))
-
- val res = model.predict(biasedTestSamples)
- Assert.assertTrue(res[0] >= res[1])
- }
-
- private fun pairwiseDifference(models: Array): Double {
- var diff = 0.0
- var total = 0.0
- for (m1 in models) {
- for (m2 in models) {
- if (m1 === m2) continue
- for (k in m1.weights.indices) {
- diff += (m1.weights[k] - m2.weights[k]).absoluteValue
- total += (m1.weights[k] + m2.weights[k]) / 2
- }
- }
- }
- return diff / total
- }
-
- @Test
- fun testGossipConvergence() {
- val model1 = Adaline(1.0, 4)
- val model11 = Adaline(1.0, 4)
-
- val model2 = Pegasos(0.1, 4, 100)
- val model22 = Pegasos(0.1, 4, 100)
-
- for (i in 0..100) {
- model1.merge(model11)
- model2.merge(model22)
-
- model11.merge(model1)
- model22.merge(model2)
- }
-
- val models1 = arrayOf(model1 as OnlineModel, model11 as OnlineModel)
- val models2 = arrayOf(model2 as OnlineModel, model22 as OnlineModel)
-
- Assert.assertTrue(pairwiseDifference(models1) < 0.1)
- Assert.assertTrue(pairwiseDifference(models2) < 0.1)
- }
-
- @Test
- fun testToyRecommendations() {
- val features = arrayOf(
- arrayOf(-1.0, 97.0, -1.0, -1.0),
- arrayOf(-1.0, 101.0, -1.0, -1.0),
- arrayOf(-1.0, -1.0, -1.0, -1.0),
- arrayOf(-1.0, 95.0, -1.0, -1.0),
- arrayOf(-1.0, 97.0, -1.0, -1.0),
- arrayOf(-1.0, 101.0, -1.0, -1.0),
- arrayOf(-1.0, 1.0, -1.0, -1.0),
- arrayOf(-1.0, 95.0, -1.0, -1.0),
- arrayOf(-1.0, 103.0, -1.0, -1.0),
- arrayOf(-1.0, 101.0, -1.0, -1.0),
- arrayOf(-1.0, 0.0, -1.0, -1.0),
- arrayOf(-1.0, 96.0, -1.0, -1.0),
- )
- val labels = arrayOf(50.0, 44.0, 0.0, 49.0, 50.0, 44.0, 0.0, 49.0, 50.0, 44.0, 0.0, 49.0)
- val model = Pegasos(0.1, 4, 100)
- model.update(features, labels)
-
- var correctPredictions = 0
-
- for (i in 0..10) {
- val test = arrayOf(
- arrayOf(-1.0, Random.nextDouble(95.0, 100.0), -1.0, -1.0),
- arrayOf(-1.0, Random.nextDouble(95.0, 100.0), -1.0, -1.0),
- arrayOf(-1.0, Random.nextDouble(-1.0, 1.0), -1.0, -1.0),
- arrayOf(-1.0, Random.nextDouble(95.0, 100.0), -1.0, -1.0),
- ).map { model.predict(it) }
-
- if (test[0] >= test[2] && test[1] >= test[2] && test[3] >= test[2]) {
- correctPredictions += 1
- }
- }
-
- Assert.assertTrue(correctPredictions >= 5)
- }
-
- /**
- * Use this to get statistics for local training data
- */
- fun calculateStats(classRanges: Array, features: Array>) {
- val means = Array>(5) { emptyArray() }
- val variance = Array>(5) { emptyArray() }
- for (i in classRanges.indices) {
- var counter = 0
- if (i > 0) {
- means[i - 1] = Array(225) { 0.0 }
- variance[i - 1] = Array(225) { 0.0 }
- val subf = features.copyOfRange(classRanges[i - 1], classRanges[i])
- for (fs in subf) {
- means[i - 1] += fs
- counter += 1
- }
- means[i - 1] = means[i - 1].map { p -> p / counter }.toTypedArray()
-
- for (fs in subf) {
- variance[i - 1] += (fs - means[i - 1]).map { p -> p.pow(2) }.toTypedArray()
- }
-
- variance[i - 1] = variance[i - 1].map { p -> sqrt(p / counter) }.toTypedArray()
- }
- }
- }
-
- @Test
- fun testRecommendations() {
- val numFeat = highVarianceFeatureLabels.size
- val classRanges = Array(6) { -1 }
- val mutFeatures: MutableList> = mutableListOf()
- var c = 0
- var f = 0
- File("superapp-essentia/test/res").listFiles()!!.forEach { classDir ->
- classRanges[c] = f
- classDir.listFiles()!!.forEach { jsonFile ->
- mutFeatures.add(featuresFromJson(jsonFile))
- f += 1
- }
- c += 1
- }
-
- classRanges[c] = f
-
- val features = mutFeatures.toTypedArray()
-
- val usersPerGroup = 3
- val models = Array(usersPerGroup * 5) { Pegasos(0.1, numFeat, 100) }
- val globalModel = Pegasos(0.1, numFeat, 100)
-
- val plays: MutableList> = mutableListOf()
- for ((i, _) in models.withIndex()) {
- val myClass = i / usersPerGroup
- // 10 random songs in-class
- val likedIdxs = Array(10) { (classRanges[myClass]..classRanges[myClass + 1]).random() }
- val likedPlays = Array(10) { (20..50).random().toDouble() }
-
- // 7 random songs out-of-class
- val mehIdxs = Array(7) {
- var idx = (0..features.size).random()
- while (classRanges[myClass] <= idx && idx < classRanges[myClass + 1]) idx = (0..features.size).random()
- idx
- }
- val mehPlays = Array(7) { (0..1).random().toDouble() }
-
- val playMap = arrayOf(*likedIdxs, *mehIdxs).zip(arrayOf(*likedPlays, *mehPlays)).toMap()
- val play = features.mapIndexed { fid, _ -> playMap[fid] ?: -1.0 }.toTypedArray()
- plays.add(play)
- }
-
- // gossip train for global model
- repeat(100) {
- for ((i, mi) in models.withIndex()) {
- for ((j, mj) in models.withIndex()) {
- val flag = Random.nextBoolean()
- if (flag) {
- mj.merge(globalModel)
- globalModel.update(features, plays[j])
- } else {
- mi.merge(globalModel)
- globalModel.update(features, plays[i])
- }
-
- // we bias our model by re-training it every time on local features
- for ((k, mk) in models.withIndex()) {
- mk.update(features, plays[k])
- mk.update(features, plays[k])
- }
- }
- }
- }
-
- val correct = models.mapIndexed { i, m ->
- val myClass = i / 5
- val preds = m.predict(features)
- val predIdx = preds.indexOfFirst { it == preds.maxOrNull()!! }
- if (classRanges[myClass] <= predIdx && predIdx < classRanges[myClass + 1]) 1 else 0
- }
- val numCorrect = correct.sum()
- Assert.assertTrue("num correct: $numCorrect", numCorrect >= 0)
- }
-
- fun featuresFromJson(jsonFile: File): Array {
- val year = -1.0
- val genre = -1.0
-
- val essentiaFeatures = JSONObject(jsonFile.bufferedReader().use { it.readText() })
-
- val lowlevel = essentiaFeatures.getJSONObject("lowlevel")
-
- val dynamic_complexity = lowlevel.getDouble("dynamic_complexity")
- val average_loudness = lowlevel.getDouble("average_loudness")
-
- var integrated_loudness = -1.0
- var loudness_range = -1.0
- var momentary = Array(5) { -1.0 }
- var short_term = Array(5) { -1.0 }
- try {
- integrated_loudness = lowlevel.getJSONObject("loudness_ebu128").getDouble("integrated")
- loudness_range = lowlevel.getJSONObject("loudness_ebu128").getDouble("loudness_range")
- momentary = stats(lowlevel.getJSONObject("loudness_ebu128").getJSONObject("momentary"))
- short_term = stats(lowlevel.getJSONObject("loudness_ebu128").getJSONObject("short_term"))
- } catch (e: Exception) {
- }
-
- val keys = arrayOf(
- "barkbands_crest", "barkbands_flatness_db", "barkbands_kurtosis", "barkbands_skewness", "barkbands_spread",
- "dissonance", "erbbands_crest", "erbbands_flatness_db", "erbbands_kurtosis", "erbbands_skewness",
- "erbbands_spread", "hfc", "melbands_crest", "melbands_flatness_db", "melbands_kurtosis", "melbands_skewness",
- "melbands_spread", "pitch_salience", "spectral_centroid", "spectral_complexity", "spectral_decrease",
- "spectral_energy", "spectral_energyband_high", "spectral_energyband_low", "spectral_energyband_middle_high",
- "spectral_energyband_middle_low", "spectral_entropy", "spectral_flux", "spectral_kurtosis", "spectral_rms",
- "spectral_rolloff", "spectral_skewness", "spectral_spread", "spectral_strongpeak", "zerocrossingrate"
- )
- val lowlevelStats = Array(keys.size) { Array(5) { -1.0 } }
- for ((i, key) in keys.withIndex()) {
- lowlevelStats[i] = stats(lowlevel.getJSONObject(key))
- }
-
- val tonal = essentiaFeatures.getJSONObject("tonal")
-
- val key = scale2label(tonal.getString("chords_key"), tonal.getString("chords_scale"))
-
- var key_edma: Array
- var key_krumhansl = arrayOf(0.0, 0.0)
- var key_temperley = arrayOf(0.0, 0.0)
- try {
- key_edma = scale2label(
- tonal.getJSONObject("key_edma").getString("key"),
- tonal.getJSONObject("key_edma").getString("scale")
- )
- key_krumhansl = scale2label(
- tonal.getJSONObject("key_krumhansl").getString("key"),
- tonal.getJSONObject("key_krumhansl").getString("scale")
- )
- key_temperley = scale2label(
- tonal.getJSONObject("key_temperley").getString("key"),
- tonal.getJSONObject("key_temperley").getString("scale")
- )
- } catch (e: Exception) {
- key_edma = scale2label(
- tonal.getString("key_key"),
- tonal.getString("key_scale")
- )
- }
-
- val chords_strength = stats(tonal.getJSONObject("chords_strength"))
- val hpcp_crest = try { stats(tonal.getJSONObject("hpcp_crest")) } catch (e: Exception) { Array(5) { -1.0 } }
- val hpcp_entropy = stats(tonal.getJSONObject("hpcp_entropy"))
- val tuning_nontempered_energy_ratio = tonal.getDouble("tuning_nontempered_energy_ratio")
- val tuning_diatonic_strength = tonal.getDouble("tuning_diatonic_strength")
-
- val rhythm: JSONObject = essentiaFeatures.getJSONObject("rhythm")
- val bpm = rhythm.getDouble("bpm")
- val danceability = rhythm.getDouble("danceability")
- val beats_loudness = stats(rhythm.getJSONObject("beats_loudness"))
-
- val metadata: JSONObject = essentiaFeatures.getJSONObject("metadata")
- val length = metadata.getJSONObject("audio_properties").getDouble("length")
- val replay_gain = metadata.getJSONObject("audio_properties").getDouble("replay_gain")
-
- val features = arrayOf(
- arrayOf(
- year, genre, length, replay_gain, dynamic_complexity, average_loudness,
- integrated_loudness, loudness_range, bpm, danceability, tuning_nontempered_energy_ratio,
- tuning_diatonic_strength
- ),
- momentary, short_term, lowlevelStats.flatten().toTypedArray(), key, key_edma, key_krumhansl, key_temperley,
- chords_strength, hpcp_crest, hpcp_entropy, beats_loudness
- ).flatten().toTypedArray()
-
- // log transform on features
- for ((i, feat) in features.withIndex()) {
- features[i] = if (feat < 0.0) -log10(-feat) else if (feat > 0.0) log10(feat) else 0.0
- }
-
- var finalFeatures = Array(highVarianceFeatureLabels.size) { 0.0 }
- for ((idx, fidx) in this.highVarianceFeatureLabels.withIndex()) {
- finalFeatures[idx] = features[fidx]
- }
-
- return finalFeatures
- }
-}
diff --git a/gossipML/superapp-essentia b/gossipML/superapp-essentia
deleted file mode 160000
index f56b302db..000000000
--- a/gossipML/superapp-essentia
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f56b302db5d4a56a1d9388c27a3c8dcd81727e4f
diff --git a/musicdao/build.gradle b/musicdao/build.gradle
index 6c36d9908..cffc08fef 100644
--- a/musicdao/build.gradle
+++ b/musicdao/build.gradle
@@ -77,7 +77,6 @@ android {
}
dependencies {
- implementation project(':gossipML')
implementation project(':common')
api(project(':ipv8-android')) {
diff --git a/peerchat/build.gradle b/peerchat/build.gradle
deleted file mode 100644
index 3a80e5c7f..000000000
--- a/peerchat/build.gradle
+++ /dev/null
@@ -1,102 +0,0 @@
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
-apply plugin: 'com.squareup.sqldelight'
-apply plugin: 'org.jlleitschuh.gradle.ktlint'
-
-ktlint {
- version = "$ktlint_version"
- android = true
- outputToConsole = true
- ignoreFailures = false
-}
-
-sqldelight {
- Database {
- packageName = "nl.tudelft.peerchat.sqldelight"
- sourceFolders = ["sqldelight"]
- schemaOutputDirectory = file("src/main/sqldelight/databases")
- }
-}
-
-android {
- compileSdkVersion 33
-
- defaultConfig {
- minSdkVersion 22
- targetSdkVersion 33
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = "1.8"
- }
-
- buildFeatures {
- viewBinding = true
- }
- namespace 'nl.tudelft.trustchain.peerchat'
-}
-
-dependencies {
- implementation project(':common')
-
- // AndroidX
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.9.0'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- implementation "androidx.recyclerview:recyclerview:1.1.0"
- implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
- implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
- implementation "androidx.fragment:fragment-ktx:$fragment_version"
- implementation "androidx.preference:preference:1.1.0"
- implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
- implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
-
- // Material
- implementation 'com.google.android.material:material:1.1.0'
- implementation 'com.getbase:floatingactionbutton:1.10.1'
-
- // Kotlin
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
- implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
-
- // Logging
- implementation 'io.github.microutils:kotlin-logging:1.7.7'
- implementation 'com.github.tony19:logback-android:2.0.0'
-
- implementation 'com.github.MattSkala:recyclerview-itemadapter:0.4'
- implementation 'com.github.bumptech.glide:glide:4.11.0'
- implementation 'androidx.legacy:legacy-support-v4:1.0.0'
- implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
- annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
-
- // Testing
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
- kotlinOptions.freeCompilerArgs += [
- "-opt-in=kotlin.RequiresOptIn"
- ]
-}
diff --git a/peerchat/doc/add_contact.png b/peerchat/doc/add_contact.png
deleted file mode 100644
index 4ba0ffa26..000000000
Binary files a/peerchat/doc/add_contact.png and /dev/null differ
diff --git a/peerchat/doc/add_nearby.png b/peerchat/doc/add_nearby.png
deleted file mode 100644
index c318f72d3..000000000
Binary files a/peerchat/doc/add_nearby.png and /dev/null differ
diff --git a/peerchat/doc/add_remote.png b/peerchat/doc/add_remote.png
deleted file mode 100644
index fb143ebb2..000000000
Binary files a/peerchat/doc/add_remote.png and /dev/null differ
diff --git a/peerchat/doc/contacts.png b/peerchat/doc/contacts.png
deleted file mode 100644
index d3fc15e00..000000000
Binary files a/peerchat/doc/contacts.png and /dev/null differ
diff --git a/peerchat/doc/conversation.png b/peerchat/doc/conversation.png
deleted file mode 100644
index dddcc8a7d..000000000
Binary files a/peerchat/doc/conversation.png and /dev/null differ
diff --git a/peerchat/src/main/AndroidManifest.xml b/peerchat/src/main/AndroidManifest.xml
deleted file mode 100644
index 628740cbf..000000000
--- a/peerchat/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/PeerChatActivity.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/PeerChatActivity.kt
deleted file mode 100644
index a9ab5dcc9..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/PeerChatActivity.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package nl.tudelft.trustchain.peerchat
-
-import nl.tudelft.trustchain.common.BaseActivity
-
-class PeerChatActivity : BaseActivity() {
- override val navigationGraph = R.navigation.nav_graph_peerchat
- override val bottomNavigationMenu = R.menu.peerchat_navigation_menu
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddContactFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddContactFragment.kt
deleted file mode 100644
index 2dd9ef48b..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddContactFragment.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.addcontact
-
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import kotlinx.android.synthetic.main.fragment_add_contact.*
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.contacts.ContactStore
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.QRCodeUtils
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.databinding.FragmentAddContactBinding
-
-class AddContactFragment : BaseFragment(R.layout.fragment_add_contact) {
- private val binding by viewBinding(FragmentAddContactBinding::bind)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val publicKeyBin = requireArguments().getString(ARG_PUBLIC_KEY)!!
- binding.txtPublicKey.text = publicKeyBin
- lifecycleScope.launch {
- val bitmap = withContext(Dispatchers.Default) {
- QRCodeUtils(requireContext()).createQR(publicKeyBin)
- }
- binding.qr.setImageBitmap(bitmap)
- }
-
- binding.btnSave.setOnClickListener {
- val name = edtName.text.toString()
- if (name.isNotEmpty()) {
- val publicKey = defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
-
- val myPublicKey = getIpv8().myPeer.publicKey.keyToBin().toHex()
- if (publicKeyBin != myPublicKey) {
- ContactStore.getInstance(requireContext())
- .addContact(publicKey, name)
- findNavController().popBackStack(R.id.contactsFragment, false)
- } else {
- Toast.makeText(requireContext(), "You cannot add yourself", Toast.LENGTH_SHORT)
- .show()
- }
- } else {
- Toast.makeText(requireContext(), "Name is empty", Toast.LENGTH_SHORT).show()
- }
- }
- }
-
- companion object {
- const val ARG_PUBLIC_KEY = "public_key"
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddNearbyFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddNearbyFragment.kt
deleted file mode 100644
index bb9c134d5..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddNearbyFragment.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.addcontact
-
-import android.content.Intent
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.QRCodeUtils
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.databinding.FragmentAddNearbyBinding
-
-class AddNearbyFragment : BaseFragment(R.layout.fragment_add_nearby) {
- private val binding by viewBinding(FragmentAddNearbyBinding::bind)
-
- private var publicKeyBin = MutableLiveData()
-
- private val qrCodeUtils by lazy {
- QRCodeUtils(requireContext())
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- publicKeyBin.value = savedInstanceState?.getString(KEY_PUBLIC_KEY)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val myPublicKey = getIpv8().myPeer.publicKey.keyToBin().toHex()
- binding.txtMyPublicKey.text = myPublicKey
- lifecycleScope.launch {
- val bitmap = withContext(Dispatchers.Default) {
- qrCodeUtils.createQR(myPublicKey)
- }
- binding.qr.setImageBitmap(bitmap)
- }
-
- binding.contactQr.setOnClickListener {
- qrCodeUtils.startQRScanner(this)
- }
-
- binding.btnContinue.setOnClickListener {
- val args = Bundle()
-
- val publicKeyBin = publicKeyBin.value
- if (publicKeyBin != null) {
- try {
- defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
- args.putString(AddContactFragment.ARG_PUBLIC_KEY, publicKeyBin)
- findNavController().navigate(
- R.id.action_addNearbyFragment_to_addContactFragment,
- args
- )
- } catch (e: Exception) {
- e.printStackTrace()
- Toast.makeText(requireContext(), "Invalid public key", Toast.LENGTH_LONG).show()
- }
- } else {
- Toast.makeText(requireContext(), "Scan the QR code", Toast.LENGTH_LONG).show()
- }
- }
-
- publicKeyBin.observe(
- viewLifecycleOwner,
- Observer { publicKeyBin ->
- binding.txtContactPublicKey.text = publicKeyBin
-
- lifecycleScope.launch {
- val bitmap = if (publicKeyBin != null) withContext(Dispatchers.Default) {
- qrCodeUtils.createQR(publicKeyBin)
- } else null
- binding.contactQr.setImageBitmap(bitmap)
- }
- }
- )
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- val publicKeyBin = qrCodeUtils.parseActivityResult(requestCode, resultCode, data)
- if (publicKeyBin != null) {
- try {
- defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
- this.publicKeyBin.value = publicKeyBin
- } catch (e: Exception) {
- e.printStackTrace()
- Toast.makeText(requireContext(), "Invalid public key", Toast.LENGTH_LONG).show()
- }
- } else {
- Toast.makeText(requireContext(), "Scan failed", Toast.LENGTH_LONG).show()
- }
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putString(KEY_PUBLIC_KEY, publicKeyBin.value)
- }
-
- companion object {
- private const val KEY_PUBLIC_KEY = "public_key"
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddRemoteFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddRemoteFragment.kt
deleted file mode 100644
index 08b381650..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/addcontact/AddRemoteFragment.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.addcontact
-
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.core.content.ContextCompat
-import androidx.navigation.fragment.findNavController
-import kotlinx.android.synthetic.main.fragment_add_remote.*
-import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.databinding.FragmentAddRemoteBinding
-
-class AddRemoteFragment : BaseFragment(R.layout.fragment_add_remote) {
- private val binding by viewBinding(FragmentAddRemoteBinding::bind)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val myPublicKey = getIpv8().myPeer.publicKey.keyToBin().toHex()
- binding.txtMyPublicKey.text = myPublicKey
-
- btnCopy.setOnClickListener {
- val clipboard =
- ContextCompat.getSystemService(requireContext(), ClipboardManager::class.java)
- val clip = ClipData.newPlainText("Public Key", myPublicKey)
- clipboard?.setPrimaryClip(clip)
- Toast.makeText(requireContext(), "Copied to clipboard", Toast.LENGTH_SHORT).show()
- }
-
- binding.btnContinue.setOnClickListener {
- val args = Bundle()
- val publicKeyBin = edtContactPublicKey.text.toString()
-
- try {
- defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
- args.putString(AddContactFragment.ARG_PUBLIC_KEY, publicKeyBin)
- findNavController().navigate(
- R.id.action_addRemoteFragment_to_addContactFragment,
- args
- )
- } catch (e: Exception) {
- e.printStackTrace()
- Toast.makeText(requireContext(), "Invalid public key", Toast.LENGTH_LONG).show()
- }
- }
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/AvatarView.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/AvatarView.kt
deleted file mode 100644
index db4d1f098..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/AvatarView.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.contacts
-
-import android.content.Context
-import android.graphics.PorterDuff
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.widget.FrameLayout
-import kotlinx.android.synthetic.main.view_avatar.view.*
-import nl.tudelft.trustchain.common.util.getColorByHash
-import nl.tudelft.trustchain.peerchat.R
-
-class AvatarView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) :
- FrameLayout(context, attrs, defStyleAttr) {
-
- init {
- LayoutInflater.from(context).inflate(R.layout.view_avatar, this, true)
- }
-
- fun setUser(id: String, name: String) {
- val initials = name.split(" ")
- .mapNotNull { it.firstOrNull() }
- .take(2)
- .joinToString("")
- txtInitials.text = initials
- imgAvatar.setColorFilter(getColorByHash(context, id), PorterDuff.Mode.MULTIPLY)
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItem.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItem.kt
deleted file mode 100644
index d9985c599..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItem.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.contacts
-
-import com.mattskala.itemadapter.Item
-import nl.tudelft.trustchain.common.contacts.Contact
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-
-data class ContactItem(
- val contact: Contact,
- val lastMessage: ChatMessage?,
- val isOnline: Boolean,
- val isBluetooth: Boolean
-) : Item() {
- override fun areItemsTheSame(other: Item): Boolean {
- return other is ContactItem && contact.mid == other.contact.mid
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItemRenderer.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItemRenderer.kt
deleted file mode 100644
index 8fb76e3e7..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactItemRenderer.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.contacts
-
-import android.graphics.Typeface
-import android.view.View
-import androidx.core.view.isVisible
-import com.mattskala.itemadapter.ItemLayoutRenderer
-import kotlinx.android.synthetic.main.item_contact.view.*
-import nl.tudelft.trustchain.common.contacts.Contact
-import nl.tudelft.trustchain.peerchat.R
-import java.text.SimpleDateFormat
-
-class ContactItemRenderer(
- private val onItemClick: (Contact) -> Unit,
- private val onItemLongClick: (Contact) -> Unit
-) : ItemLayoutRenderer(
- ContactItem::class.java
-) {
- private val dateFormat = SimpleDateFormat.getDateTimeInstance()
-
- override fun bindView(item: ContactItem, view: View) = with(view) {
- txtName.text = item.contact.name
- // txtName.isVisible = item.contact.name.isNotEmpty()
- txtPeerId.text = item.contact.mid
- val lastMessageDate = item.lastMessage?.timestamp
- txtDate.isVisible = lastMessageDate != null
- txtDate.text = if (lastMessageDate != null) dateFormat.format(lastMessageDate) else null
- txtMessage.isVisible = item.lastMessage != null
- txtMessage.text = item.lastMessage?.message
- val textStyle = if (item.lastMessage?.read == false) Typeface.BOLD else Typeface.NORMAL
- txtMessage.setTypeface(null, textStyle)
- setOnClickListener {
- onItemClick(item.contact)
- }
- imgWifi.isVisible = item.isOnline
- imgBluetooth.isVisible = item.isBluetooth
- avatar.setUser(item.contact.mid, item.contact.name)
- setOnLongClickListener {
- onItemLongClick(item.contact)
- true
- }
- }
-
- override fun getLayoutResourceId(): Int {
- return R.layout.item_contact
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactsFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactsFragment.kt
deleted file mode 100644
index 85b74af82..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/contacts/ContactsFragment.kt
+++ /dev/null
@@ -1,219 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.contacts
-
-import android.bluetooth.BluetoothManager
-import android.content.res.ColorStateList
-import android.net.ConnectivityManager
-import android.os.Build
-import android.os.Bundle
-import android.text.InputType
-import android.view.View
-import android.widget.EditText
-import android.widget.LinearLayout
-import androidx.appcompat.app.AlertDialog
-import androidx.core.content.ContextCompat.getColor
-import androidx.core.content.ContextCompat.getSystemService
-import androidx.core.content.getSystemService
-import androidx.core.view.isVisible
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
-import androidx.lifecycle.asLiveData
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.mattskala.itemadapter.Item
-import com.mattskala.itemadapter.ItemAdapter
-import kotlinx.android.synthetic.main.fragment_contacts.*
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.isActive
-import nl.tudelft.ipv8.Peer
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.contacts.Contact
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.databinding.FragmentContactsBinding
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.ui.conversation.ConversationFragment
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class ContactsFragment : BaseFragment(R.layout.fragment_contacts) {
- private val binding by viewBinding(FragmentContactsBinding::bind)
-
- private val adapter = ItemAdapter()
-
- private val store by lazy {
- PeerChatStore.getInstance(requireContext())
- }
-
- private val peers = MutableStateFlow>(listOf())
-
- private val items: LiveData> by lazy {
- combine(store.getContactsWithLastMessages(), peers) { contacts, peers ->
- createItems(contacts, peers)
- }.asLiveData()
- }
-
- private fun getPeerChatCommunity(): PeerChatCommunity {
- return getIpv8().getOverlay()
- ?: throw java.lang.IllegalStateException("PeerChatCommunity is not configured")
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- adapter.registerRenderer(
- ContactItemRenderer(
- {
- val args = Bundle()
- args.putString(ConversationFragment.ARG_PUBLIC_KEY, it.publicKey.keyToBin().toHex())
- args.putString(ConversationFragment.ARG_NAME, it.name)
- findNavController().navigate(R.id.action_contactsFragment_to_conversationFragment, args)
- },
- {
- showOptions(it)
- }
- )
- )
-
- lifecycleScope.launchWhenResumed {
- while (isActive) {
- // Refresh peer status periodically
- peers.value = getPeerChatCommunity().getPeers()
- updateConnectivityStatus()
- delay(1000L)
- }
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.recyclerView.adapter = adapter
- binding.recyclerView.layoutManager = LinearLayoutManager(context)
- binding.recyclerView.addItemDecoration(
- DividerItemDecoration(
- context,
- LinearLayout.VERTICAL
- )
- )
-
- binding.btnAddNearby.setOnClickListener {
- findNavController().navigate(R.id.action_contactsFragment_to_addNearbyFragment)
- fab.collapse()
- }
-
- binding.btnAddRemote.setOnClickListener {
- findNavController().navigate(R.id.action_contactsFragment_to_addRemoteFragment)
- fab.collapse()
- }
-
- items.observe(
- viewLifecycleOwner,
- Observer {
- adapter.updateItems(it)
- binding.imgEmpty.isVisible = it.isEmpty()
- }
- )
- }
-
- private fun createItems(
- contacts: List>,
- peers: List
- ): List- {
- return contacts.filter { it.first.publicKey != getIpv8().myPeer.publicKey }
- .map { contactWithMessage ->
- val (contact, message) = contactWithMessage
- val peer = peers.find { it.mid == contact.mid }
- ContactItem(
- contact,
- message,
- peer != null && !peer.address.isEmpty(),
- peer?.bluetoothAddress != null
- )
- }
- }
-
- @Suppress("DEPRECATION")
- private fun getConnectivityStatus(): Pair {
- val cm = getSystemService(requireContext(), ConnectivityManager::class.java)
- val activeNetwork = cm!!.activeNetworkInfo
- return if (activeNetwork != null) { // connected to the internet
- val type = if (activeNetwork.subtypeName.isNotBlank())
- activeNetwork.subtypeName else activeNetwork.typeName
- Pair(type, activeNetwork.isConnected)
- } else {
- Pair("Internet", false)
- }
- }
-
- private fun getBluetoothStatus(): Boolean {
- val bluetoothManager = requireContext().getSystemService()
- ?: throw IllegalStateException("BluetoothManager not found")
- val bluetoothAdapter = bluetoothManager.adapter
- return bluetoothAdapter?.isEnabled ?: false
- }
-
- private fun updateConnectivityStatus() {
- val (networkType, isConnected) = getConnectivityStatus()
- binding.btnInternet.text = when (networkType) {
- "WIFI" -> "Wi-Fi"
- else -> networkType
- }
- val green = getColor(requireContext(), R.color.green)
- val red = getColor(requireContext(), R.color.red)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- val color = if (isConnected) green else red
- binding.btnInternet.compoundDrawableTintList = ColorStateList.valueOf(color)
- }
- val bluetoothStatus = getBluetoothStatus()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- val color = if (bluetoothStatus) green else red
- binding.btnBluetooth.compoundDrawableTintList = ColorStateList.valueOf(color)
- }
- }
-
- private fun showOptions(contact: Contact) {
- val items = arrayOf("Rename", "Delete")
- AlertDialog.Builder(requireContext())
- .setItems(items) { _, which ->
- when (which) {
- 0 -> renameContact(contact)
- 1 -> deleteContact(contact)
- }
- }
- .show()
- }
-
- private fun renameContact(contact: Contact) {
- val builder = AlertDialog.Builder(requireContext())
- builder.setTitle("Rename Contact")
-
- // Set up the input
- val input = EditText(requireContext())
- input.inputType = InputType.TYPE_CLASS_TEXT
- input.setText(contact.name)
- builder.setView(input)
-
- // Set up the buttons
- builder.setPositiveButton(
- "Rename"
- ) { _, _ ->
- store.contactsStore.updateContact(contact.publicKey, input.text.toString())
- }
- builder.setNegativeButton(
- "Cancel"
- ) { dialog, _ -> dialog.cancel() }
-
- builder.show()
- }
-
- private fun deleteContact(contact: Contact) {
- store.contactsStore.deleteContact(contact)
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItem.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItem.kt
deleted file mode 100644
index 383cc0bb3..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItem.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
-
-import com.mattskala.itemadapter.Item
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-
-data class ChatMessageItem(
- val chatMessage: ChatMessage,
- val transaction: TrustChainBlock?,
- val shouldShowAvatar: Boolean,
- val shouldShowDate: Boolean,
- val participantName: String
-) : Item() {
- override fun areItemsTheSame(other: Item): Boolean {
- return other is ChatMessageItem && other.chatMessage.id == chatMessage.id
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItemRenderer.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItemRenderer.kt
deleted file mode 100644
index 04ec1387a..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ChatMessageItemRenderer.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
-
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.text.format.DateUtils
-import android.view.Gravity
-import android.view.View
-import android.widget.FrameLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.view.isVisible
-import androidx.core.view.updateLayoutParams
-import com.bumptech.glide.Glide
-import com.mattskala.itemadapter.ItemLayoutRenderer
-import kotlinx.android.synthetic.main.item_message.view.*
-import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
-import nl.tudelft.trustchain.common.util.getColorByHash
-import nl.tudelft.trustchain.peerchat.R
-import java.math.BigInteger
-import java.text.SimpleDateFormat
-
-class ChatMessageItemRenderer : ItemLayoutRenderer(
- ChatMessageItem::class.java
-) {
-
- private val dateTimeFormat = SimpleDateFormat.getDateTimeInstance()
- private val timeFormat = SimpleDateFormat.getTimeInstance()
- private val constraintSet = ConstraintSet()
-
- override fun bindView(item: ChatMessageItem, view: View) = with(view) {
- txtMessage.text = item.chatMessage.message
- if (item.chatMessage.outgoing) {
- txtMessage.gravity = Gravity.START
- } else {
- txtMessage.gravity = Gravity.END
- }
- item.transaction?.transaction?.let {
- txtTransaction.text =
- TransactionRepository.prettyAmount((item.transaction.transaction["amount"] as BigInteger).toLong())
- if (item.chatMessage.message.isEmpty()) {
- txtMessage.visibility = View.GONE
- }
- }
- val color = getColorByHash(context, item.chatMessage.sender.toString())
- val newColor = Color.argb(50, Color.red(color), Color.green(color), Color.blue(color))
- txtMessage.backgroundTintList = ColorStateList.valueOf(newColor)
- txtDate.text = if (DateUtils.isToday(item.chatMessage.timestamp.time))
- timeFormat.format(item.chatMessage.timestamp)
- else dateTimeFormat.format(item.chatMessage.timestamp)
- txtDate.isVisible = item.shouldShowDate
- avatar.setUser(item.chatMessage.sender.toString(), item.participantName)
- avatar.isVisible = item.shouldShowAvatar
- imgDelivered.isVisible = item.chatMessage.outgoing && item.chatMessage.ack
- constraintSet.clone(constraintLayout)
- constraintSet.removeFromHorizontalChain(content.id)
- constraintSet.removeFromHorizontalChain(bottomContainer.id)
-
- val attachment = item.chatMessage.attachment
- if (attachment != null && attachment.type == MessageAttachment.TYPE_IMAGE) {
- val file = attachment.getFile(view.context)
- if (file.exists()) {
- Glide.with(view).load(file).into(image)
- progress.isVisible = false
- } else {
- image.setImageBitmap(null)
- progress.isVisible = true
- }
- image.isVisible = true
- txtMessage.isVisible = false
- txtTransaction.isVisible = false
- } else if (item.chatMessage.transactionHash != null) {
- progress.isVisible =
- item.transaction == null // transaction not yet received via trustchain
-
- txtMessage.isVisible = item.chatMessage.message.isNotBlank()
-
- image.isVisible = false
- txtTransaction.isVisible = true
- } else {
- image.isVisible = false
- txtMessage.isVisible = true
- progress.isVisible = false
- txtTransaction.isVisible = false
- }
-
- if (item.chatMessage.outgoing) {
- innerContent.updateLayoutParams {
- gravity = Gravity.END
- }
- constraintSet.connect(
- content.id,
- ConstraintSet.START,
- ConstraintSet.PARENT_ID,
- ConstraintSet.START
- )
- constraintSet.connect(
- content.id,
- ConstraintSet.END,
- ConstraintSet.PARENT_ID,
- ConstraintSet.END
- )
- constraintSet.connect(
- bottomContainer.id,
- ConstraintSet.END,
- ConstraintSet.PARENT_ID,
- ConstraintSet.END
- )
- } else {
- val avatarMargin = resources.getDimensionPixelSize(R.dimen.avatar_size) +
- resources.getDimensionPixelSize(R.dimen.avatar_margin)
- innerContent.updateLayoutParams {
- gravity = Gravity.START
- }
- constraintSet.connect(
- content.id,
- ConstraintSet.START,
- ConstraintSet.PARENT_ID,
- ConstraintSet.START,
- avatarMargin
- )
- constraintSet.connect(
- content.id,
- ConstraintSet.END,
- ConstraintSet.PARENT_ID,
- ConstraintSet.END
- )
- constraintSet.connect(
- bottomContainer.id,
- ConstraintSet.START,
- ConstraintSet.PARENT_ID,
- ConstraintSet.START,
- avatarMargin
- )
- }
- constraintSet.applyTo(constraintLayout)
- }
-
- override fun getLayoutResourceId(): Int {
- return R.layout.item_message
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ConversationFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ConversationFragment.kt
deleted file mode 100644
index 2d01aa24f..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/ConversationFragment.kt
+++ /dev/null
@@ -1,218 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
-
-import android.app.Activity
-import android.content.Intent
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import androidx.core.view.inputmethod.InputConnectionCompat
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
-import androidx.lifecycle.asLiveData
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.mattskala.itemadapter.Item
-import com.mattskala.itemadapter.ItemAdapter
-import kotlinx.android.synthetic.main.fragment_conversation.*
-import kotlinx.coroutines.flow.map
-import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.trustchain.common.eurotoken.GatewayStore
-import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.databinding.FragmentConversationBinding
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.util.saveFile
-
-class ConversationFragment : BaseFragment(R.layout.fragment_conversation) {
- private val binding by viewBinding(FragmentConversationBinding::bind)
-
- private val adapter = ItemAdapter()
-
- private val items: LiveData
> by lazy {
- store.getAllByPublicKey(publicKey).map { messages ->
- createItems(messages)
- }.asLiveData()
- }
-
- private val store by lazy {
- PeerChatStore.getInstance(requireContext())
- }
-
- private val gatewayStore by lazy {
- GatewayStore.getInstance(requireContext())
- }
-
- private val transactionRepository by lazy {
- TransactionRepository(getTrustChainCommunity(), gatewayStore)
- }
-
- private val publicKeyBin by lazy {
- requireArguments().getString(ARG_PUBLIC_KEY)!!
- }
-
- private val publicKey by lazy {
- defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
- }
-
- private val name by lazy {
- requireArguments().getString(ARG_NAME)!!
- }
-
- private val onCommitContentListener =
- InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, _ ->
- val lacksPermission = (
- flags and
- InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
- ) != 0
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) {
- try {
- inputContentInfo.requestPermission()
- } catch (e: Exception) {
- return@OnCommitContentListener false // return false if failed
- }
- }
-
- val uri = inputContentInfo.contentUri
- Log.d("ConversationFragment", "uri: $uri")
-
- sendImageFromUri(uri)
-
- true
- }
-
- private fun getPeerChatCommunity(): PeerChatCommunity {
- return getIpv8().getOverlay()
- ?: throw java.lang.IllegalStateException("PeerChatCommunity is not configured")
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- adapter.registerRenderer(ChatMessageItemRenderer())
-
- items.observe(
- this,
- Observer {
- val oldCount = adapter.itemCount
- adapter.updateItems(it)
- if (adapter.itemCount != oldCount) {
- // New message, scroll to the bottom
- binding.recyclerView.scrollToPosition(adapter.itemCount - 1)
- }
- }
- )
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.recyclerView.adapter = adapter
- binding.recyclerView.layoutManager = LinearLayoutManager(context)
-
- binding.edtMessage.onCommitContentListener = onCommitContentListener
-
- edtMessage.setOnFocusChangeListener { _, hasFocus ->
- if (hasFocus) {
- fab.collapse()
- }
- }
-
- /*
- btnRequestMoney.setOnClickListener {
- val args = Bundle()
- fab.collapse()
- args.putString(TransferFragment.ARG_PUBLIC_KEY, publicKeyBin)
- args.putString(TransferFragment.ARG_NAME, name)
- args.putBoolean(TransferFragment.ARG_IS_REQUEST, true)
- findNavController().navigate(
- R.id.action_conversationFragment_to_transferFragment,
- args
- )
- }
- */
-
- btnSendMoney.setOnClickListener {
- val args = Bundle()
- fab.collapse()
- args.putString(TransferFragment.ARG_PUBLIC_KEY, publicKeyBin)
- args.putString(TransferFragment.ARG_NAME, name)
- args.putBoolean(TransferFragment.ARG_IS_REQUEST, false)
- findNavController().navigate(
- R.id.action_conversationFragment_to_transferFragment,
- args
- )
- }
-
- btnSend.setOnClickListener {
- val message = binding.edtMessage.text.toString()
- if (message.isNotEmpty()) {
- getPeerChatCommunity().sendMessage(message, publicKey)
- binding.edtMessage.text = null
- }
- }
-
- btnAddImage.setOnClickListener {
- val intent = Intent()
- fab.collapse()
- intent.type = "image/*"
- intent.action = Intent.ACTION_GET_CONTENT
- @Suppress("DEPRECATION")
- startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE)
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- @Suppress("DEPRECATION")
- when (requestCode) {
- PICK_IMAGE -> if (resultCode == Activity.RESULT_OK && data != null) {
- val uri = data.data
- if (uri != null) {
- sendImageFromUri(uri)
- }
- }
- else -> super.onActivityResult(requestCode, resultCode, data)
- }
- }
-
- private fun createItems(messages: List): List- {
- return messages.mapIndexed { index, chatMessage ->
- val shouldShowAvatar = !chatMessage.outgoing && (
- index == messages.size - 1 ||
- messages[index + 1].outgoing != chatMessage.outgoing
- )
- /*
- val shouldShowDate = (index == messages.size - 1 ||
- messages[index + 1].outgoing != chatMessage.outgoing ||
- (messages[index + 1].timestamp.time - chatMessage.timestamp.time > GROUP_TIME_LIMIT))
- */
- val shouldShowDate = true
- ChatMessageItem(
- chatMessage,
- transactionRepository.getTransactionWithHash(chatMessage.transactionHash),
- shouldShowAvatar,
- shouldShowDate,
- name
- )
- }
- }
-
- private fun sendImageFromUri(uri: Uri) {
- val file = saveFile(requireContext(), uri)
- getPeerChatCommunity().sendImage(file, publicKey)
- }
-
- companion object {
- const val ARG_PUBLIC_KEY = "public_key"
- const val ARG_NAME = "name"
-
- private const val GROUP_TIME_LIMIT = 60 * 1000
- private const val PICK_IMAGE = 10
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/RichEditText.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/RichEditText.kt
deleted file mode 100644
index 17b7777da..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/RichEditText.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputConnection
-import androidx.appcompat.R
-import androidx.core.view.inputmethod.EditorInfoCompat
-import androidx.core.view.inputmethod.InputConnectionCompat
-
-class RichEditText @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = R.attr.editTextStyle
-) : androidx.appcompat.widget.AppCompatEditText(context, attrs, defStyleAttr) {
- var onCommitContentListener: InputConnectionCompat.OnCommitContentListener? = null
-
- override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection {
- val ic: InputConnection = super.onCreateInputConnection(editorInfo)
- EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/*"))
-
- val callback =
- InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts ->
- onCommitContentListener?.onCommitContent(inputContentInfo, flags, opts) ?: false
- }
-
- @Suppress("DEPRECATION")
- return InputConnectionCompat.createWrapper(ic, editorInfo, callback)
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/TransferFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/TransferFragment.kt
deleted file mode 100644
index e06894021..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/TransferFragment.kt
+++ /dev/null
@@ -1,162 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
-
-import android.os.Bundle
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.View
-import android.widget.EditText
-import android.widget.Toast
-import androidx.navigation.fragment.findNavController
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.eurotoken.GatewayStore
-import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.databinding.TransferFragmentBinding
-
-class TransferFragment : BaseFragment(R.layout.transfer_fragment) {
-
- private val binding by viewBinding(TransferFragmentBinding::bind)
-
- private fun getPeerChatCommunity(): PeerChatCommunity {
- return getIpv8().getOverlay()
- ?: throw java.lang.IllegalStateException("PeerChatCommunity is not configured")
- }
-
- private val transactionRepository by lazy {
- TransactionRepository(getTrustChainCommunity(), GatewayStore.getInstance(requireContext()))
- }
-
- private val publicKeyBin by lazy {
- requireArguments().getString(ARG_PUBLIC_KEY)!!
- }
-
- private val publicKey by lazy {
- defaultCryptoProvider.keyFromPublicBin(publicKeyBin.hexToBytes())
- }
-
- private val ownPublicKey by lazy {
- defaultCryptoProvider.keyFromPublicBin(
- getPeerChatCommunity().myPeer.publicKey.keyToBin().toHex().hexToBytes()
- )
- }
-
- private val name by lazy {
- requireArguments().getString(ARG_NAME)!!
- }
-
- private val isRequest by lazy {
- requireArguments().getString(ARG_IS_REQUEST)
- }
-
- private fun sendMoneyMessage(amount: Long, message: String) {
- CoroutineScope(Dispatchers.IO).launch {
- val block = transactionRepository.sendTransferProposalSync(publicKey.keyToBin(), amount)
- if (block == null) {
- Toast.makeText(requireContext(), "Insufficient balance", Toast.LENGTH_LONG).show()
- } else {
- getPeerChatCommunity().sendMessageWithTransaction(message, block.calculateHash(), publicKey)
- }
- }
- }
-
- private fun requestMoney(amount: Long) {
- val message = "GIB MONEY, $amount"
- if (message.isNotEmpty()) {
- getPeerChatCommunity().sendMessage(message, publicKey)
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val isRequest = requireArguments().getBoolean(ARG_IS_REQUEST)
-
- binding.txtBalance.text =
- TransactionRepository.prettyAmount(transactionRepository.getMyVerifiedBalance())
-
- if (isRequest) {
- binding.btnTransfer.text = "Request money"
- } else {
- binding.btnTransfer.text = "Transfer money"
- }
-
- binding.txtOwnPublicKey.text = ownPublicKey.toString()
-
- binding.edtAmount.addDecimalLimiter()
-
- binding.btnTransfer.setOnClickListener {
- val amount = getAmount(binding.edtAmount.text.toString())
- val message = binding.edtMessage.text.toString()
- if (isRequest) {
- requestMoney(amount)
- } else {
- sendMoneyMessage(amount, message)
- }
- findNavController().navigateUp()
- }
-
- binding.txtContactName.text = name
- binding.txtContactPublicKey.text = publicKey.toString()
- binding.avatar.setUser(publicKey.toString(), name)
- }
-
- companion object {
- const val ARG_PUBLIC_KEY = "public_key"
- const val ARG_NAME = "name"
- const val ARG_IS_REQUEST = "is_request"
-
- fun getAmount(amount: String): Long {
- val regex = """[^\d]""".toRegex()
- return regex.replace(amount, "").toLong()
- }
-
- fun EditText.decimalLimiter(string: String): String {
-
- var amount = getAmount(string)
-
- if (amount == 0L) {
- return ""
- }
-
- // val amount = string.replace("[^\\d]", "").toLong()
- return (amount / 100).toString() + "." + (amount % 100).toString().padStart(2, '0')
- }
-
- fun EditText.addDecimalLimiter() {
-
- this.addTextChangedListener(object : TextWatcher {
-
- override fun afterTextChanged(s: Editable?) {
- val str = this@addDecimalLimiter.text!!.toString()
- if (str.isEmpty()) return
- val str2 = decimalLimiter(str)
-
- if (str2 != str) {
- this@addDecimalLimiter.setText(str2)
- val pos = this@addDecimalLimiter.text!!.length
- this@addDecimalLimiter.setSelection(pos)
- }
- }
-
- override fun beforeTextChanged(
- s: CharSequence?,
- start: Int,
- count: Int,
- after: Int
- ) {
- }
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- }
- })
- }
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/FeedFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/FeedFragment.kt
deleted file mode 100644
index 7c47599f3..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/FeedFragment.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.feed
-
-import android.os.Bundle
-import android.view.View
-import android.widget.LinearLayout
-import android.widget.Toast
-import androidx.core.view.isVisible
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.liveData
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.mattskala.itemadapter.Item
-import com.mattskala.itemadapter.ItemAdapter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.isActive
-import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.databinding.FragmentFeedBinding
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class FeedFragment : BaseFragment(R.layout.fragment_feed) {
- private val binding by viewBinding(FragmentFeedBinding::bind)
-
- private val store by lazy {
- PeerChatStore.getInstance(requireContext())
- }
-
- private val postRepository by lazy {
- PostRepository(getIpv8().getOverlay()!!, store)
- }
-
- private val adapter = ItemAdapter()
-
- private val items: LiveData
> by lazy {
- liveData { emit(listOf- ()) }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- adapter.registerRenderer(
- PostItemRenderer(
- {
- if (!postRepository.likePost(it.block)) {
- Toast.makeText(requireContext(), "You already liked this post", Toast.LENGTH_SHORT)
- .show()
- }
- },
- {
- val args = Bundle()
- args.putString(NewPostFragment.ARG_HASH, it.block.calculateHash().toHex())
- findNavController().navigate(R.id.action_feedFragment_to_newPostFragment, args)
- }
- )
- )
-
- lifecycleScope.launchWhenResumed {
- while (isActive) {
- // Refresh peer status periodically
- val items = postRepository.getPostsByFriends()
- adapter.updateItems(items)
- binding.imgEmpty.isVisible = items.isEmpty()
- delay(1000L)
- }
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.recyclerView.adapter = adapter
- binding.recyclerView.layoutManager = LinearLayoutManager(context)
- binding.recyclerView.addItemDecoration(
- DividerItemDecoration(
- context,
- LinearLayout.VERTICAL
- )
- )
-
- binding.fab.setOnClickListener {
- findNavController().navigate(R.id.action_feedFragment_to_newPostFragment)
- }
-
- items.observe(
- viewLifecycleOwner,
- Observer {
- adapter.updateItems(it)
- binding.imgEmpty.isVisible = it.isEmpty()
- }
- )
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/NewPostFragment.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/NewPostFragment.kt
deleted file mode 100644
index 1da711a21..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/NewPostFragment.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.feed
-
-import android.os.Bundle
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.widget.Toast
-import androidx.navigation.fragment.findNavController
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.trustchain.common.ui.BaseFragment
-import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.R
-import nl.tudelft.trustchain.peerchat.databinding.FragmentNewPostBinding
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class NewPostFragment : BaseFragment(R.layout.fragment_new_post) {
- private val binding by viewBinding(FragmentNewPostBinding::bind)
-
- private val store by lazy {
- PeerChatStore.getInstance(requireContext())
- }
-
- private val postRepository by lazy {
- PostRepository(getIpv8().getOverlay()!!, store)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setHasOptionsMenu(true)
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- inflater.inflate(R.menu.options_new_post, menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.post -> {
- val text = binding.edtPost.text.toString()
- if (text.isBlank()) {
- Toast.makeText(context, "Your post is empty", Toast.LENGTH_SHORT).show()
- } else {
- val hash = arguments?.getString(ARG_HASH)?.hexToBytes()
- if (hash != null) {
- postRepository.createReply(hash, text)
- Toast.makeText(context, "Your reply has been created", Toast.LENGTH_SHORT)
- .show()
- } else {
- postRepository.createPost(text)
- Toast.makeText(context, "Your post has been created", Toast.LENGTH_SHORT)
- .show()
- }
- findNavController().navigateUp()
- }
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- companion object {
- val ARG_HASH = "hash"
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItem.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItem.kt
deleted file mode 100644
index fa68d0ff5..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItem.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.feed
-
-import com.mattskala.itemadapter.Item
-import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
-import nl.tudelft.trustchain.common.contacts.Contact
-
-data class PostItem(
- val block: TrustChainBlock,
- val contact: Contact?,
- val linkedBlock: TrustChainBlock?,
- val linkedContact: Contact?,
- val replies: List,
- val likes: List,
- /**
- * True if I liked this post.
- */
- val liked: Boolean
-) : Item()
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItemRenderer.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItemRenderer.kt
deleted file mode 100644
index 00b289be0..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostItemRenderer.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.feed
-
-import android.view.View
-import androidx.core.content.res.ResourcesCompat
-import androidx.core.view.isVisible
-import com.mattskala.itemadapter.ItemLayoutRenderer
-import kotlinx.android.synthetic.main.item_post.view.*
-import nl.tudelft.trustchain.peerchat.R
-import java.text.SimpleDateFormat
-
-class PostItemRenderer(
- private val onLike: (PostItem) -> Unit,
- private val onComment: (PostItem) -> Unit
-) : ItemLayoutRenderer(
- PostItem::class.java
-) {
- private val dateFormat = SimpleDateFormat.getDateTimeInstance()
-
- override fun bindView(item: PostItem, view: View) = with(view) {
- val prefix = if (item.linkedContact != null) "@" + item.linkedContact.name + ": " else ""
- val text = item.block.transaction[PostRepository.KEY_TEXT] as? String
- txtMessage.text = prefix + text
-
- val lastMessageDate = item.block.timestamp
- txtDate.text = dateFormat.format(lastMessageDate)
-
- txtName.text = item.contact?.name
-
- txtPeerId.text = item.contact?.mid
-
- if (item.contact != null) {
- avatar.setUser(item.contact.mid, item.contact.name)
- }
-
- btnLike.setOnClickListener {
- onLike(item)
- }
-
- btnComment.setOnClickListener {
- onComment(item)
- }
-
- txtCommentCount.text = item.replies.size.toString()
- txtLikeCount.text = item.likes.size.toString()
-
- val likeColor = if (item.liked)
- ResourcesCompat.getColor(view.resources, R.color.colorPrimary, null) else
- ResourcesCompat.getColor(view.resources, R.color.text_secondary, null)
-
- btnLike.setColorFilter(likeColor)
- txtLikeCount.setTextColor(likeColor)
-
- btnComment.isVisible = item.block.isProposal
- txtCommentCount.isVisible = item.block.isProposal
-
- /*
- // txtName.isVisible = item.contact.name.isNotEmpty()
- txtPeerId.text = item.contact.mid
- txtMessage.isVisible = item.lastMessage != null
- txtMessage.text = item.lastMessage?.message
- val textStyle = if (item.lastMessage?.read == false) Typeface.BOLD else Typeface.NORMAL
- txtMessage.setTypeface(null, textStyle)
- setOnClickListener {
- onItemClick(item.contact)
- }
- imgWifi.isVisible = item.isOnline
- imgBluetooth.isVisible = item.isBluetooth
- avatar.setUser(item.contact.mid, item.contact.name)
- setOnLongClickListener {
- onItemLongClick(item.contact)
- true
- }
- */
- }
-
- override fun getLayoutResourceId(): Int {
- return R.layout.item_post
- }
-}
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostRepository.kt b/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostRepository.kt
deleted file mode 100644
index 4fb0f21a7..000000000
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/feed/PostRepository.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-package nl.tudelft.trustchain.peerchat.ui.feed
-
-import kotlinx.coroutines.flow.first
-import nl.tudelft.ipv8.Peer
-import nl.tudelft.ipv8.android.IPv8Android
-import nl.tudelft.ipv8.attestation.trustchain.*
-import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainStore
-import nl.tudelft.trustchain.common.contacts.Contact
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-
-class PostRepository(
- private val trustChainCommunity: TrustChainCommunity,
- private val peerChatStore: PeerChatStore
-) {
- fun createPost(text: String): TrustChainBlock {
- val transaction = mapOf(
- KEY_TEXT to text
- )
- return trustChainCommunity.createProposalBlock(
- BLOCK_TYPE_POST, transaction,
- ANY_COUNTERPARTY_PK
- )
- }
-
- fun createReply(blockHash: ByteArray, text: String): TrustChainBlock? {
- val transaction = mapOf(
- KEY_TEXT to text
- )
- val block = trustChainCommunity.database.getBlockWithHash(blockHash)
- return if (block != null) {
- trustChainCommunity.createAgreementBlock(block, transaction)
- } else {
- null
- }
- }
-
- fun likePost(block: TrustChainBlock): Boolean {
- return if (!hasLikedPost(block)) {
- LikeBlockBuilder(
- trustChainCommunity.myPeer,
- trustChainCommunity.database, block
- ).sign()
-// trustChainCommunity.onBlockCreated(likeBlock) TODO create a public method for this
- true
- } else {
- false
- }
- }
-
- /**
- * Returns true if the current user has liked the post.
- */
- fun hasLikedPost(block: TrustChainBlock): Boolean {
- val myPeer = IPv8Android.getInstance().myPeer
- val linkedBlocks = trustChainCommunity.database
- .getAllLinked(block)
- return linkedBlocks.find {
- it.type == BLOCK_TYPE_LIKE &&
- it.publicKey.contentEquals(myPeer.publicKey.keyToBin())
- } != null
- }
-
- private fun findContactByPublicKey(contacts: List, publicKeyBin: ByteArray): Contact? {
- val myPeer = IPv8Android.getInstance().myPeer
- val myContact = Contact("You", myPeer.publicKey)
- return if (publicKeyBin.contentEquals(myPeer.publicKey.keyToBin()))
- myContact
- else contacts.find {
- it.publicKey.keyToBin().contentEquals(publicKeyBin)
- }
- }
-
- suspend fun getPostsByFriends(): List {
- val myPeer = IPv8Android.getInstance().myPeer
- val contacts = peerChatStore.contactsStore.getContacts().first()
- val posts = trustChainCommunity.database
- .getBlocksWithType(BLOCK_TYPE_POST)
- .sortedByDescending { it.insertTime }
- val likes = trustChainCommunity.database
- .getBlocksWithType(BLOCK_TYPE_LIKE)
- .sortedByDescending { it.insertTime }
- return posts.map { post ->
- val contact = findContactByPublicKey(contacts, post.publicKey)
- val replies = posts.filter { it.linkedBlockId == post.blockId }
- val postLikes = likes.filter { it.linkedBlockId == post.blockId }
- val liked = postLikes.find {
- it.publicKey.contentEquals(myPeer.publicKey.keyToBin())
- } != null
- val linkedBlock = posts.find { it.blockId == post.linkedBlockId }
- val linkedContact = findContactByPublicKey(contacts, post.linkPublicKey)
- PostItem(post, contact, linkedBlock, linkedContact, replies, postLikes, liked)
- }
- }
-
- companion object {
- private const val BLOCK_TYPE_POST = "post"
- private const val BLOCK_TYPE_LIKE = "like"
- const val KEY_TEXT = "text"
- }
-
- class LikeBlockBuilder(
- myPeer: Peer,
- database: TrustChainStore,
- private val link: TrustChainBlock
- ) : BlockBuilder(myPeer, database) {
- override fun update(builder: TrustChainBlock.Builder) {
- builder.type = BLOCK_TYPE_LIKE
- builder.rawTransaction = TransactionEncoding.encode(mapOf())
- builder.linkPublicKey = link.publicKey
- builder.linkSequenceNumber = link.sequenceNumber
- }
- }
-}
diff --git a/peerchat/src/main/res/drawable-hdpi/ic_delivered.png b/peerchat/src/main/res/drawable-hdpi/ic_delivered.png
deleted file mode 100644
index f8294b69d..000000000
Binary files a/peerchat/src/main/res/drawable-hdpi/ic_delivered.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-hdpi/ic_sent.png b/peerchat/src/main/res/drawable-hdpi/ic_sent.png
deleted file mode 100644
index 437d9d778..000000000
Binary files a/peerchat/src/main/res/drawable-hdpi/ic_sent.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-mdpi/ic_delivered.png b/peerchat/src/main/res/drawable-mdpi/ic_delivered.png
deleted file mode 100644
index dccbdb68c..000000000
Binary files a/peerchat/src/main/res/drawable-mdpi/ic_delivered.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-mdpi/ic_sent.png b/peerchat/src/main/res/drawable-mdpi/ic_sent.png
deleted file mode 100644
index 8bc3a5d68..000000000
Binary files a/peerchat/src/main/res/drawable-mdpi/ic_sent.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xhdpi/ic_delivered.png b/peerchat/src/main/res/drawable-xhdpi/ic_delivered.png
deleted file mode 100644
index 2950eb98d..000000000
Binary files a/peerchat/src/main/res/drawable-xhdpi/ic_delivered.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xhdpi/ic_sent.png b/peerchat/src/main/res/drawable-xhdpi/ic_sent.png
deleted file mode 100644
index 693b584ed..000000000
Binary files a/peerchat/src/main/res/drawable-xhdpi/ic_sent.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xxhdpi/ic_delivered.png b/peerchat/src/main/res/drawable-xxhdpi/ic_delivered.png
deleted file mode 100644
index 6ac1e717f..000000000
Binary files a/peerchat/src/main/res/drawable-xxhdpi/ic_delivered.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xxhdpi/ic_sent.png b/peerchat/src/main/res/drawable-xxhdpi/ic_sent.png
deleted file mode 100644
index bdbf5f28f..000000000
Binary files a/peerchat/src/main/res/drawable-xxhdpi/ic_sent.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xxxhdpi/ic_delivered.png b/peerchat/src/main/res/drawable-xxxhdpi/ic_delivered.png
deleted file mode 100644
index 7a4a928ec..000000000
Binary files a/peerchat/src/main/res/drawable-xxxhdpi/ic_delivered.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable-xxxhdpi/ic_sent.png b/peerchat/src/main/res/drawable-xxxhdpi/ic_sent.png
deleted file mode 100644
index 0d5f423dc..000000000
Binary files a/peerchat/src/main/res/drawable-xxxhdpi/ic_sent.png and /dev/null differ
diff --git a/peerchat/src/main/res/drawable/bg_field.xml b/peerchat/src/main/res/drawable/bg_field.xml
deleted file mode 100644
index 64ae6cbab..000000000
--- a/peerchat/src/main/res/drawable/bg_field.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/bg_message.xml b/peerchat/src/main/res/drawable/bg_message.xml
deleted file mode 100644
index c979c4b47..000000000
--- a/peerchat/src/main/res/drawable/bg_message.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/circle_step.xml b/peerchat/src/main/res/drawable/circle_step.xml
deleted file mode 100644
index a97fcb117..000000000
--- a/peerchat/src/main/res/drawable/circle_step.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_add_photo_alternate_24.xml b/peerchat/src/main/res/drawable/ic_baseline_add_photo_alternate_24.xml
deleted file mode 100644
index 190b26813..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_add_photo_alternate_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_dynamic_feed_24.xml b/peerchat/src/main/res/drawable/ic_baseline_dynamic_feed_24.xml
deleted file mode 100644
index 71d8fc5ab..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_dynamic_feed_24.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_mode_comment_24.xml b/peerchat/src/main/res/drawable/ic_baseline_mode_comment_24.xml
deleted file mode 100644
index 84c28ee32..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_mode_comment_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_people_24.xml b/peerchat/src/main/res/drawable/ic_baseline_people_24.xml
deleted file mode 100644
index 71da7d97d..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_people_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_person_24.xml b/peerchat/src/main/res/drawable/ic_baseline_person_24.xml
deleted file mode 100644
index 07eeb5ad8..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_person_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_reply_24.xml b/peerchat/src/main/res/drawable/ic_baseline_reply_24.xml
deleted file mode 100644
index bd35ee9e7..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_reply_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_request_money_24.xml b/peerchat/src/main/res/drawable/ic_baseline_request_money_24.xml
deleted file mode 100644
index 899ac532f..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_request_money_24.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- >
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_send_money_24.xml b/peerchat/src/main/res/drawable/ic_baseline_send_money_24.xml
deleted file mode 100644
index b5b3a0bb1..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_send_money_24.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_baseline_thumb_up_24.xml b/peerchat/src/main/res/drawable/ic_baseline_thumb_up_24.xml
deleted file mode 100644
index 146f85cee..000000000
--- a/peerchat/src/main/res/drawable/ic_baseline_thumb_up_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_bluetooth_black_24dp.xml b/peerchat/src/main/res/drawable/ic_bluetooth_black_24dp.xml
deleted file mode 100644
index 4b0b71eaf..000000000
--- a/peerchat/src/main/res/drawable/ic_bluetooth_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_camera_alt_black_24dp.xml b/peerchat/src/main/res/drawable/ic_camera_alt_black_24dp.xml
deleted file mode 100644
index 946934cf0..000000000
--- a/peerchat/src/main/res/drawable/ic_camera_alt_black_24dp.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_cloud_off_black_24dp.xml b/peerchat/src/main/res/drawable/ic_cloud_off_black_24dp.xml
deleted file mode 100644
index 94909d21c..000000000
--- a/peerchat/src/main/res/drawable/ic_cloud_off_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_content_copy_black_24dp.xml b/peerchat/src/main/res/drawable/ic_content_copy_black_24dp.xml
deleted file mode 100644
index 380c8783e..000000000
--- a/peerchat/src/main/res/drawable/ic_content_copy_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_link_white_24dp.xml b/peerchat/src/main/res/drawable/ic_link_white_24dp.xml
deleted file mode 100644
index c5acaaa98..000000000
--- a/peerchat/src/main/res/drawable/ic_link_white_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_person_add_black_24dp.xml b/peerchat/src/main/res/drawable/ic_person_add_black_24dp.xml
deleted file mode 100644
index 9c533518c..000000000
--- a/peerchat/src/main/res/drawable/ic_person_add_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_photo_camera_white_24dp.xml b/peerchat/src/main/res/drawable/ic_photo_camera_white_24dp.xml
deleted file mode 100644
index d7a3c0f9b..000000000
--- a/peerchat/src/main/res/drawable/ic_photo_camera_white_24dp.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_send_black_24dp.xml b/peerchat/src/main/res/drawable/ic_send_black_24dp.xml
deleted file mode 100644
index fdf1c9009..000000000
--- a/peerchat/src/main/res/drawable/ic_send_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/ic_wifi_black_24dp.xml b/peerchat/src/main/res/drawable/ic_wifi_black_24dp.xml
deleted file mode 100644
index f0f6f477a..000000000
--- a/peerchat/src/main/res/drawable/ic_wifi_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/peerchat/src/main/res/drawable/indicator_offline.xml b/peerchat/src/main/res/drawable/indicator_offline.xml
deleted file mode 100644
index e9424ceaa..000000000
--- a/peerchat/src/main/res/drawable/indicator_offline.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/drawable/indicator_online.xml b/peerchat/src/main/res/drawable/indicator_online.xml
deleted file mode 100644
index 8f42993a9..000000000
--- a/peerchat/src/main/res/drawable/indicator_online.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_add_contact.xml b/peerchat/src/main/res/layout/fragment_add_contact.xml
deleted file mode 100644
index c56ea2be7..000000000
--- a/peerchat/src/main/res/layout/fragment_add_contact.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_add_nearby.xml b/peerchat/src/main/res/layout/fragment_add_nearby.xml
deleted file mode 100644
index 674f43047..000000000
--- a/peerchat/src/main/res/layout/fragment_add_nearby.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_add_remote.xml b/peerchat/src/main/res/layout/fragment_add_remote.xml
deleted file mode 100644
index 0c6676302..000000000
--- a/peerchat/src/main/res/layout/fragment_add_remote.xml
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_contacts.xml b/peerchat/src/main/res/layout/fragment_contacts.xml
deleted file mode 100644
index f305d8095..000000000
--- a/peerchat/src/main/res/layout/fragment_contacts.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_conversation.xml b/peerchat/src/main/res/layout/fragment_conversation.xml
deleted file mode 100644
index 3e185c159..000000000
--- a/peerchat/src/main/res/layout/fragment_conversation.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_feed.xml b/peerchat/src/main/res/layout/fragment_feed.xml
deleted file mode 100644
index f3a4150ac..000000000
--- a/peerchat/src/main/res/layout/fragment_feed.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/fragment_new_post.xml b/peerchat/src/main/res/layout/fragment_new_post.xml
deleted file mode 100644
index 4e0a1c1d5..000000000
--- a/peerchat/src/main/res/layout/fragment_new_post.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/item_contact.xml b/peerchat/src/main/res/layout/item_contact.xml
deleted file mode 100644
index bb0f18461..000000000
--- a/peerchat/src/main/res/layout/item_contact.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/item_message.xml b/peerchat/src/main/res/layout/item_message.xml
deleted file mode 100644
index e06d84601..000000000
--- a/peerchat/src/main/res/layout/item_message.xml
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/item_post.xml b/peerchat/src/main/res/layout/item_post.xml
deleted file mode 100644
index c38dca3f9..000000000
--- a/peerchat/src/main/res/layout/item_post.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/transfer_fragment.xml b/peerchat/src/main/res/layout/transfer_fragment.xml
deleted file mode 100644
index 48e1d8591..000000000
--- a/peerchat/src/main/res/layout/transfer_fragment.xml
+++ /dev/null
@@ -1,197 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/layout/view_avatar.xml b/peerchat/src/main/res/layout/view_avatar.xml
deleted file mode 100644
index de93797dc..000000000
--- a/peerchat/src/main/res/layout/view_avatar.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/menu/options_new_post.xml b/peerchat/src/main/res/menu/options_new_post.xml
deleted file mode 100644
index 1f991382f..000000000
--- a/peerchat/src/main/res/menu/options_new_post.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/peerchat/src/main/res/menu/peerchat_navigation_menu.xml b/peerchat/src/main/res/menu/peerchat_navigation_menu.xml
deleted file mode 100644
index 5b052932d..000000000
--- a/peerchat/src/main/res/menu/peerchat_navigation_menu.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
diff --git a/peerchat/src/main/res/navigation/nav_graph_peerchat.xml b/peerchat/src/main/res/navigation/nav_graph_peerchat.xml
deleted file mode 100644
index a25473b63..000000000
--- a/peerchat/src/main/res/navigation/nav_graph_peerchat.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/res/values/dimens.xml b/peerchat/src/main/res/values/dimens.xml
deleted file mode 100644
index 2f5fa2fab..000000000
--- a/peerchat/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 52dp
- 16dp
-
diff --git a/peerchat/src/main/res/values/strings.xml b/peerchat/src/main/res/values/strings.xml
deleted file mode 100644
index 88913309f..000000000
--- a/peerchat/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- €
-
diff --git a/peerchat/src/main/res/values/styles.xml b/peerchat/src/main/res/values/styles.xml
deleted file mode 100644
index e54ac6739..000000000
--- a/peerchat/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/peerchat/src/main/sqldelight/2.sqm b/peerchat/src/main/sqldelight/2.sqm
deleted file mode 100644
index 290d10806..000000000
--- a/peerchat/src/main/sqldelight/2.sqm
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE messages ADD COLUMN transaction_hash BLOB;
diff --git a/peerchat/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt b/peerchat/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt
deleted file mode 100644
index 487585659..000000000
--- a/peerchat/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.tudelft.trustchain.debug
-
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, (2 + 2))
- }
-}
diff --git a/settings.gradle b/settings.gradle
index 0cc8bc75d..80c4d8a9d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -21,7 +21,5 @@ include ':eurotoken'
include ':freedomOfComputing'
include ':musicdao'
include ':musicdao-datafeeder'
-include ':gossipML'
-include ':peerchat'
include ':valuetransfer'
include ':geth-android'
diff --git a/valuetransfer/build.gradle b/valuetransfer/build.gradle
index 5ee702373..bd59b814c 100644
--- a/valuetransfer/build.gradle
+++ b/valuetransfer/build.gradle
@@ -97,7 +97,6 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "androidx.room:room-runtime:$room_version"
- implementation project(path: ':peerchat')
implementation project(path: ':eurotoken')
implementation 'com.google.android.gms:play-services-maps:18.0.0'
implementation 'com.google.android.gms:play-services-location:18.0.0'
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ValueTransferMainActivity.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ValueTransferMainActivity.kt
index b643b8a3c..bc340d81a 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ValueTransferMainActivity.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ValueTransferMainActivity.kt
@@ -62,11 +62,11 @@ import nl.tudelft.trustchain.common.valuetransfer.extensions.decodeBytes
import nl.tudelft.trustchain.common.valuetransfer.extensions.imageBytes
import nl.tudelft.trustchain.common.valuetransfer.extensions.resize
import nl.tudelft.trustchain.eurotoken.community.EuroTokenCommunity
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.community.PeerChatCommunity
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.community.IdentityCommunity
import nl.tudelft.trustchain.valuetransfer.db.IdentityStore
import nl.tudelft.trustchain.valuetransfer.dialogs.IdentityAttestationConfirmDialog
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AckPayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AckPayload.kt
similarity index 93%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AckPayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AckPayload.kt
index 241b6e1e7..7ad475076 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AckPayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AckPayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import nl.tudelft.ipv8.messaging.Deserializable
import nl.tudelft.ipv8.messaging.Serializable
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentPayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentPayload.kt
similarity index 95%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentPayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentPayload.kt
index 1854c3cfb..dcb94666a 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentPayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentPayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import nl.tudelft.ipv8.messaging.Deserializable
import nl.tudelft.ipv8.messaging.Serializable
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentRequestPayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentRequestPayload.kt
similarity index 94%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentRequestPayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentRequestPayload.kt
index 1071a945e..0ee66c444 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/AttachmentRequestPayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/AttachmentRequestPayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import nl.tudelft.ipv8.messaging.Deserializable
import nl.tudelft.ipv8.messaging.Serializable
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactConnectPayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactConnectPayload.kt
similarity index 97%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactConnectPayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactConnectPayload.kt
index 04ecd3afd..8d18fbe85 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactConnectPayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactConnectPayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImagePayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImagePayload.kt
similarity index 96%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImagePayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImagePayload.kt
index 0dbe6098c..fcacf9f93 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImagePayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImagePayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import android.graphics.Bitmap
import nl.tudelft.ipv8.keyvault.PublicKey
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImageRequestPayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImageRequestPayload.kt
similarity index 95%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImageRequestPayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImageRequestPayload.kt
index 5a3f27c16..b18cedffd 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/ContactImageRequestPayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/ContactImageRequestPayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/MessagePayload.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/MessagePayload.kt
similarity index 98%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/MessagePayload.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/MessagePayload.kt
index ae2e3beb7..d0a4b9fdd 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/MessagePayload.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/MessagePayload.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import mu.KotlinLogging
import nl.tudelft.ipv8.messaging.*
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/PeerChatCommunity.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/PeerChatCommunity.kt
similarity index 94%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/PeerChatCommunity.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/PeerChatCommunity.kt
index d13432b30..10d1bed7d 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/community/PeerChatCommunity.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/community/PeerChatCommunity.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.community
+package nl.tudelft.trustchain.valuetransfer.community
import android.content.Context
import android.database.sqlite.SQLiteConstraintException
@@ -21,10 +21,10 @@ import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityAttribute
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityInfo
import nl.tudelft.trustchain.common.valuetransfer.entity.TransferRequest
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
@@ -245,7 +245,8 @@ class PeerChatCommunity(
json.put("address_line", addressLine)
val serialized = json.toString().toByteArray()
- val attachment = MessageAttachment(MessageAttachment.TYPE_LOCATION, serialized.size.toLong(), serialized)
+ val attachment =
+ MessageAttachment(MessageAttachment.TYPE_LOCATION, serialized.size.toLong(), serialized)
val chatMessage = createOutgoingChatMessage(addressLine, attachment, null, recipient, identityInfo)
database.addMessage(chatMessage)
@@ -260,7 +261,11 @@ class PeerChatCommunity(
) {
val serialized = transferRequest.serialize()
- val attachment = MessageAttachment(MessageAttachment.TYPE_TRANSFER_REQUEST, serialized.size.toLong(), serialized)
+ val attachment = MessageAttachment(
+ MessageAttachment.TYPE_TRANSFER_REQUEST,
+ serialized.size.toLong(),
+ serialized
+ )
val chatMessage = createOutgoingChatMessage(description ?: "", attachment, null, recipient, identityInfo)
database.addMessage(chatMessage)
@@ -274,7 +279,8 @@ class PeerChatCommunity(
) {
val serialized = contact.serialize()
val contactMessage = "${contact.name} ${contact.publicKey.keyToBin().toHex()}"
- val attachment = MessageAttachment(MessageAttachment.TYPE_CONTACT, serialized.size.toLong(), serialized)
+ val attachment =
+ MessageAttachment(MessageAttachment.TYPE_CONTACT, serialized.size.toLong(), serialized)
val chatMessage = createOutgoingChatMessage(contactMessage, attachment, null, recipient, identityInfo)
database.addMessage(chatMessage)
@@ -358,7 +364,8 @@ class PeerChatCommunity(
fun sendContactImage(peer: Peer, contactImage: ContactImage) {
Log.d("VTLOG", "SEND CONTACT IMAGE")
- val payload = ContactImagePayload(contactImage.publicKey, contactImage.imageHash, contactImage.image)
+ val payload =
+ ContactImagePayload(contactImage.publicKey, contactImage.imageHash, contactImage.image)
val packet = serializePacket(MessageId.CONTACT_IMAGE, payload, encrypt = true, recipient = peer)
logger.debug { "-> $payload" }
send(peer, packet)
@@ -369,34 +376,34 @@ class PeerChatCommunity(
*/
private fun onMessagePacket(packet: Packet) {
val (peer, payload) = packet.getDecryptedAuthPayload(
- MessagePayload.Deserializer, myPeer.key as PrivateKey
+ MessagePayload, myPeer.key as PrivateKey
)
logger.debug { "<- $payload, ${payload.transactionHash}" }
onMessage(peer, payload)
}
private fun onAckPacket(packet: Packet) {
- val (peer, payload) = packet.getAuthPayload(AckPayload.Deserializer)
+ val (peer, payload) = packet.getAuthPayload(AckPayload)
logger.debug { "<- $payload" }
onAck(peer, payload)
}
private fun onAttachmentRequestPacket(packet: Packet) {
- val (peer, payload) = packet.getAuthPayload(AttachmentRequestPayload.Deserializer)
+ val (peer, payload) = packet.getAuthPayload(AttachmentRequestPayload)
logger.debug { "<- $payload" }
onAttachmentRequest(peer, payload)
}
private fun onAttachmentPacket(packet: Packet) {
val (_, payload) = packet.getDecryptedAuthPayload(
- AttachmentPayload.Deserializer, myPeer.key as PrivateKey
+ AttachmentPayload, myPeer.key as PrivateKey
)
logger.debug { "<- $payload" }
onAttachment(payload)
}
private fun onContactImageRequestPacket(packet: Packet) {
- val (peer, _) = packet.getAuthPayload(ContactImageRequestPayload.Deserializer)
+ val (peer, _) = packet.getAuthPayload(ContactImageRequestPayload)
logger.debug { "<- $peer" }
onContactImageRequest(peer)
@@ -406,7 +413,7 @@ class PeerChatCommunity(
Log.d("VTLOG", "CONTACT IMAGE PACKET RECEIVED")
val (peer, payload) = packet.getDecryptedAuthPayload(
- ContactImagePayload.Deserializer, myPeer.key as PrivateKey
+ ContactImagePayload, myPeer.key as PrivateKey
)
Log.d("VTLOG", "CONTACT IMAGE CONTENTS PEER: $peer")
@@ -500,7 +507,13 @@ class PeerChatCommunity(
private fun onContactImage(payload: ContactImagePayload) {
Log.d("VTLOG", "ON CONTACT IMAGE with Payload image hash: ${payload.imageHash}")
if (this::onContactImageCallback.isInitialized) {
- this.onContactImageCallback(ContactImage(payload.publicKey, payload.imageHash, payload.image))
+ this.onContactImageCallback(
+ ContactImage(
+ payload.publicKey,
+ payload.imageHash,
+ payload.image
+ )
+ )
return
}
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ChatMediaDialog.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ChatMediaDialog.kt
index d7f094b8e..dc6a3f5e4 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ChatMediaDialog.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ChatMediaDialog.kt
@@ -12,8 +12,8 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mattskala.itemadapter.ItemAdapter
import nl.tudelft.ipv8.keyvault.PublicKey
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ui.VTDialogFragment
import java.text.SimpleDateFormat
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ContactInfoDialog.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ContactInfoDialog.kt
index ad0672126..c56c7a156 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ContactInfoDialog.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/dialogs/ContactInfoDialog.kt
@@ -30,9 +30,9 @@ import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.eurotoken.Transaction
import nl.tudelft.trustchain.common.util.getColorByHash
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityAttribute
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.ContactState
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ui.VTDialogFragment
import nl.tudelft.trustchain.valuetransfer.ui.contacts.ContactChatFragment
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTDialogFragment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTDialogFragment.kt
index b03c556bf..a48a5d48f 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTDialogFragment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTDialogFragment.kt
@@ -8,8 +8,8 @@ import nl.tudelft.trustchain.common.eurotoken.GatewayStore
import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
import nl.tudelft.trustchain.common.util.TrustChainHelper
import nl.tudelft.trustchain.eurotoken.community.EuroTokenCommunity
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.community.PeerChatCommunity
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.community.IdentityCommunity
import nl.tudelft.trustchain.valuetransfer.db.IdentityStore
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTFragment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTFragment.kt
index 2c8fdf28f..bd97dfa04 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTFragment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/VTFragment.kt
@@ -8,8 +8,8 @@ import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
import nl.tudelft.trustchain.common.ui.BaseFragment
import nl.tudelft.trustchain.common.util.TrustChainHelper
import nl.tudelft.trustchain.eurotoken.community.EuroTokenCommunity
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.community.PeerChatCommunity
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.community.IdentityCommunity
import nl.tudelft.trustchain.valuetransfer.db.IdentityStore
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItem.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItem.kt
index 5e694a1b2..9d9b076f1 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItem.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItem.kt
@@ -2,9 +2,9 @@ package nl.tudelft.trustchain.valuetransfer.ui.contacts
import com.mattskala.itemadapter.Item
import nl.tudelft.trustchain.common.contacts.Contact
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.ContactState
data class ChatItem(
val contact: Contact,
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItemRenderer.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItemRenderer.kt
index 499fc49f6..096410acc 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItemRenderer.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatItemRenderer.kt
@@ -8,7 +8,7 @@ import kotlinx.android.synthetic.main.item_contacts_chat.view.ivIdenticon
import nl.tudelft.ipv8.util.toHex
import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.util.getColorByHash
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.util.generateIdenticon
import java.text.SimpleDateFormat
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaDetailAdapter.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaDetailAdapter.kt
index 821ed9ad6..ddf4e054a 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaDetailAdapter.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaDetailAdapter.kt
@@ -11,7 +11,7 @@ import android.widget.TextView
import androidx.viewpager.widget.PagerAdapter
import com.bumptech.glide.Glide
import com.jsibbold.zoomage.ZoomageView
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.util.OnSwipeTouchListener
import nl.tudelft.trustchain.valuetransfer.util.getFormattedSize
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaItemRenderer.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaItemRenderer.kt
index 26e89c68a..c08af281e 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaItemRenderer.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ChatMediaItemRenderer.kt
@@ -8,7 +8,7 @@ import androidx.core.view.isVisible
import com.bumptech.glide.Glide
import com.mattskala.itemadapter.ItemLayoutRenderer
import kotlinx.android.synthetic.main.item_contact_chat_media_gallery.view.*
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
class ChatMediaItemRenderer(
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatFragment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatFragment.kt
index 61a4cf2c4..5be21bfab 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatFragment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatFragment.kt
@@ -40,18 +40,18 @@ import nl.tudelft.ipv8.util.toHex
import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.util.getColorByHash
import nl.tudelft.trustchain.common.util.viewBinding
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
-import nl.tudelft.trustchain.peerchat.util.saveFile
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.saveFile
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ui.VTFragment
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.databinding.FragmentContactsChatBinding
import nl.tudelft.trustchain.valuetransfer.dialogs.*
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityAttribute
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.ContactState
import nl.tudelft.trustchain.valuetransfer.ui.QRScanController
import nl.tudelft.trustchain.valuetransfer.ui.settings.AppPreferences
import nl.tudelft.trustchain.valuetransfer.util.*
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItem.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItem.kt
index 2a28b5ae4..8760e374f 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItem.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItem.kt
@@ -3,7 +3,7 @@ package nl.tudelft.trustchain.valuetransfer.ui.contacts
import com.mattskala.itemadapter.Item
import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
import nl.tudelft.ipv8.messaging.eva.TransferProgress
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
data class ContactChatItem(
val chatMessage: ChatMessage,
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItemRenderer.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItemRenderer.kt
index 809082c4b..a1e56ffba 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItemRenderer.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactChatItemRenderer.kt
@@ -15,12 +15,12 @@ import nl.tudelft.ipv8.messaging.eva.TransferState
import nl.tudelft.ipv8.util.toHex
import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.util.getColorByHash
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityAttribute
import nl.tudelft.trustchain.common.valuetransfer.entity.TransferRequest
-import nl.tudelft.trustchain.peerchat.community.PeerChatCommunity
+import nl.tudelft.trustchain.valuetransfer.community.PeerChatCommunity
import nl.tudelft.trustchain.valuetransfer.util.formatBalance
import nl.tudelft.trustchain.valuetransfer.util.generateIdenticon
import nl.tudelft.trustchain.valuetransfer.util.getColorIDFromThemeAttribute
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactsFragment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactsFragment.kt
index 62642c0a7..853751a20 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactsFragment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/contacts/ContactsFragment.kt
@@ -20,9 +20,9 @@ import nl.tudelft.ipv8.util.toHex
import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.util.viewBinding
import nl.tudelft.trustchain.common.valuetransfer.extensions.exitEnterView
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.ContactState
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ui.VTFragment
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/exchange/ExchangeTransactionItemRenderer.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/exchange/ExchangeTransactionItemRenderer.kt
index 08176c862..80963c333 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/exchange/ExchangeTransactionItemRenderer.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/exchange/ExchangeTransactionItemRenderer.kt
@@ -8,7 +8,7 @@ import com.mattskala.itemadapter.ItemLayoutRenderer
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.trustchain.common.contacts.ContactStore
import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.util.formatBalance
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/settings/NotificationHandler.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/settings/NotificationHandler.kt
index c9f8871b6..a96c4d538 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/settings/NotificationHandler.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/settings/NotificationHandler.kt
@@ -21,9 +21,9 @@ import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
import nl.tudelft.trustchain.common.util.getColorByHash
import nl.tudelft.trustchain.common.valuetransfer.extensions.setPadding
import nl.tudelft.trustchain.common.valuetransfer.extensions.toSquare
-import nl.tudelft.trustchain.peerchat.db.PeerChatStore
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.PeerChatStore
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.ui.QRScanController
diff --git a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/walletoverview/WalletOverviewFragment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/walletoverview/WalletOverviewFragment.kt
index 4eb64c526..d65107a21 100644
--- a/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/walletoverview/WalletOverviewFragment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/ui/walletoverview/WalletOverviewFragment.kt
@@ -21,9 +21,9 @@ import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.util.QRCodeUtils
import nl.tudelft.trustchain.common.util.viewBinding
import nl.tudelft.trustchain.common.valuetransfer.extensions.decodeImage
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
+import nl.tudelft.trustchain.valuetransfer.util.ChatMessage
+import nl.tudelft.trustchain.valuetransfer.util.ContactImage
+import nl.tudelft.trustchain.valuetransfer.util.ContactState
import nl.tudelft.trustchain.valuetransfer.R
import nl.tudelft.trustchain.valuetransfer.ValueTransferMainActivity
import nl.tudelft.trustchain.valuetransfer.databinding.FragmentWalletVtBinding
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ChatMessage.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ChatMessage.kt
similarity index 92%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ChatMessage.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ChatMessage.kt
index d2d394857..f58f39ce8 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ChatMessage.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ChatMessage.kt
@@ -1,8 +1,8 @@
-package nl.tudelft.trustchain.peerchat.entity
+package nl.tudelft.trustchain.valuetransfer.util
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityInfo
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
+import nl.tudelft.trustchain.valuetransfer.util.MessageAttachment
import java.util.*
data class ChatMessage(
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactImage.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactImage.kt
similarity index 97%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactImage.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactImage.kt
index 3e16d9a13..dc8872a41 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactImage.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactImage.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.entity
+package nl.tudelft.trustchain.valuetransfer.util
import android.graphics.Bitmap
import nl.tudelft.ipv8.keyvault.PublicKey
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactState.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactState.kt
similarity index 91%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactState.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactState.kt
index 830ce6d1e..b1b2a3f48 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/entity/ContactState.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/ContactState.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.entity
+package nl.tudelft.trustchain.valuetransfer.util
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityInfo
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/util/FileUtils.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/FileUtils.kt
similarity index 84%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/util/FileUtils.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/FileUtils.kt
index 960f4cbd0..b07e7697d 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/util/FileUtils.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/FileUtils.kt
@@ -1,10 +1,9 @@
-package nl.tudelft.trustchain.peerchat.util
+package nl.tudelft.trustchain.valuetransfer.util
import android.content.Context
import android.net.Uri
import nl.tudelft.ipv8.util.sha256
import nl.tudelft.ipv8.util.toHex
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
import java.io.File
import java.io.FileOutputStream
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/MessageAttachment.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/MessageAttachment.kt
similarity index 96%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/MessageAttachment.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/MessageAttachment.kt
index 49d448f40..76ae9d29f 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/ui/conversation/MessageAttachment.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/MessageAttachment.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.ui.conversation
+package nl.tudelft.trustchain.valuetransfer.util
import android.content.Context
import nl.tudelft.ipv8.util.toHex
diff --git a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/db/PeerChatStore.kt b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/PeerChatStore.kt
similarity index 98%
rename from peerchat/src/main/java/nl/tudelft/trustchain/peerchat/db/PeerChatStore.kt
rename to valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/PeerChatStore.kt
index 196c3f8ff..1790f7249 100644
--- a/peerchat/src/main/java/nl/tudelft/trustchain/peerchat/db/PeerChatStore.kt
+++ b/valuetransfer/src/main/java/nl/tudelft/trustchain/valuetransfer/util/PeerChatStore.kt
@@ -1,4 +1,4 @@
-package nl.tudelft.trustchain.peerchat.db
+package nl.tudelft.trustchain.valuetransfer.util
import android.content.Context
import android.graphics.BitmapFactory
@@ -11,14 +11,10 @@ import kotlinx.coroutines.flow.combine
import nl.tudelft.ipv8.keyvault.PublicKey
import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
import nl.tudelft.ipv8.util.hexToBytes
-import nl.tudelft.peerchat.sqldelight.Database
+import nl.tudelft.valuetransfer.sqldelight.Database
import nl.tudelft.trustchain.common.contacts.Contact
import nl.tudelft.trustchain.common.contacts.ContactStore
import nl.tudelft.trustchain.common.valuetransfer.entity.IdentityInfo
-import nl.tudelft.trustchain.peerchat.entity.ChatMessage
-import nl.tudelft.trustchain.peerchat.entity.ContactImage
-import nl.tudelft.trustchain.peerchat.entity.ContactState
-import nl.tudelft.trustchain.peerchat.ui.conversation.MessageAttachment
import java.util.*
class PeerChatStore(context: Context) {
diff --git a/valuetransfer/src/main/res/values/styles.xml b/valuetransfer/src/main/res/values/styles.xml
index a50d7a2aa..09d8f2b4e 100644
--- a/valuetransfer/src/main/res/values/styles.xml
+++ b/valuetransfer/src/main/res/values/styles.xml
@@ -1,6 +1,11 @@
+
+
// ACTION BAR TEXT STYLE