diff --git a/app/build.gradle b/app/build.gradle index b7e8713b9..1c6d1e2b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,6 +6,8 @@ apply plugin: 'org.jlleitschuh.gradle.ktlint' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'kotlinx-serialization' +apply plugin: "dagger.hilt.android.plugin" +apply plugin: "kotlin-kapt" buildscript { repositories { @@ -15,6 +17,8 @@ buildscript { dependencies { classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' + classpath("com.google.dagger:hilt-android-gradle-plugin:$hilt_ver") + } } @@ -103,7 +107,6 @@ allprojects { repositories { mavenCentral() jcenter() - } } @@ -181,6 +184,18 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + // Hilt + implementation("com.google.dagger:hilt-android:$hilt_ver") + implementation("androidx.hilt:hilt-navigation-fragment:1.0.0") + implementation("androidx.hilt:hilt-navigation-compose:1.1.0-alpha01") + kapt("com.google.dagger:hilt-android-compiler:$hilt_ver") + + // Room + implementation "androidx.room:room-runtime:$room_version" + implementation("androidx.room:room-ktx:$room_version") + annotationProcessor "androidx.room:room-compiler:$room_version" + kapt("androidx.room:room-compiler:$room_version") } diff --git a/musicdao/src/main/res/drawable/border.xml b/app/src/debug/res/drawable/ic_launcher_background.xml similarity index 57% rename from musicdao/src/main/res/drawable/border.xml rename to app/src/debug/res/drawable/ic_launcher_background.xml index f5dbc351f..56b70c0c0 100644 --- a/musicdao/src/main/res/drawable/border.xml +++ b/app/src/debug/res/drawable/ic_launcher_background.xml @@ -1,8 +1,6 @@ - - + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e2a229a6d..32b3656f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -151,17 +151,12 @@ android:windowSoftInputMode="stateAlwaysHidden" /> - - - - + android:screenOrientation="portrait" + android:theme="@style/Theme.MusicDaoTheme"> diff --git a/app/src/main/java/nl/tudelft/trustchain/app/AppDefinition.kt b/app/src/main/java/nl/tudelft/trustchain/app/AppDefinition.kt index 4c044df56..1942e953c 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/AppDefinition.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/AppDefinition.kt @@ -3,7 +3,7 @@ package nl.tudelft.trustchain.app import android.app.Activity import androidx.annotation.ColorRes import androidx.annotation.DrawableRes -import com.example.musicdao.MusicService +import nl.tudelft.trustchain.musicdao.MusicActivity import nl.tudelft.trustchain.FOC.MainActivityFOC import nl.tudelft.trustchain.atomicswap.AtomicSwapActivity import nl.tudelft.trustchain.common.R @@ -101,7 +101,7 @@ enum class AppDefinition( android.R.drawable.ic_media_play, "MusicDAO", R.color.black, - MusicService::class.java + MusicActivity::class.java ), EUROTOKEN( R.drawable.ic_baseline_euro_symbol_24, diff --git a/app/src/main/java/nl/tudelft/trustchain/app/AppLoader.kt b/app/src/main/java/nl/tudelft/trustchain/app/AppLoader.kt index adfb0ddeb..b32703fcf 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/AppLoader.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/AppLoader.kt @@ -1,5 +1,6 @@ package nl.tudelft.trustchain.app +import android.annotation.SuppressLint import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit @@ -58,6 +59,7 @@ class AppLoader( } } + @SuppressLint("NewApi") private suspend fun setPreferredAppList(newPreferences: Set) { dataStore.edit { settings -> settings[PREFERRED_APPS] = newPreferences 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 2faa59f4a..edec87c89 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt @@ -13,9 +13,14 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.preferencesDataStore import androidx.preference.PreferenceManager -import com.example.musicdao.ipv8.MusicCommunity +import nl.tudelft.trustchain.musicdao.core.ipv8.MusicCommunity +import nl.tudelft.trustchain.musicdao.core.dao.DaoCommunity import com.squareup.sqldelight.android.AndroidSqliteDriver import com.squareup.sqldelight.db.SqlDriver +import dagger.hilt.android.HiltAndroidApp +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -71,7 +76,9 @@ import nl.tudelft.gossipML.sqldelight.Database as MLDatabase val Context.dataStore: DataStore by preferencesDataStore(name = "settings") +@OptIn(DelicateCoroutinesApi::class) @ExperimentalUnsignedTypes +@HiltAndroidApp class TrustChainApplication : Application() { var isFirstRun: Boolean = false @@ -104,6 +111,7 @@ class TrustChainApplication : Application() { createAtomicSwapCommunity(), createMarketCommunity(), createCoinCommunity(), + createDaoCommunity(), createVotingCommunity(), createMusicCommunity(), createLiteratureCommunity(), @@ -358,6 +366,16 @@ class TrustChainApplication : Application() { ) } + private fun createDaoCommunity(): OverlayConfiguration { + val randomWalk = RandomWalk.Factory() + val nsd = NetworkServiceDiscovery.Factory(getSystemService()!!) + + return OverlayConfiguration( + Overlay.Factory(DaoCommunity::class.java), + listOf(randomWalk, nsd) + ) + } + private fun createCoinCommunity(): OverlayConfiguration { val randomWalk = RandomWalk.Factory() val nsd = NetworkServiceDiscovery.Factory(getSystemService()!!) @@ -381,7 +399,8 @@ class TrustChainApplication : Application() { private fun createMusicCommunity(): OverlayConfiguration { val settings = TrustChainSettings() - val driver = AndroidSqliteDriver(Database.Schema, this, "music.db") + // TODO: Re-concile this community with Reccomender Community + val driver = AndroidSqliteDriver(Database.Schema, this, "music-private.db") val store = TrustChainSQLiteStore(Database(driver)) val randomWalk = RandomWalk.Factory() return OverlayConfiguration( diff --git a/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardActivity.kt b/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardActivity.kt index 3443ba995..33de16945 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardActivity.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardActivity.kt @@ -108,6 +108,7 @@ class DashboardActivity : AppCompatActivity() { } } + @Suppress("DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { SETTINGS_INTENT_CODE -> { diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index a33e88f05..f071386de 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -7,5 +7,8 @@ 131.180.27.224 + + + 95.179.182.243 diff --git a/app/src/test/java/nl/tudelft/trustchain/app/ExampleUnitTest.kt b/app/src/test/java/nl/tudelft/trustchain/app/ExampleUnitTest.kt index c25bb7a66..7005ab69e 100644 --- a/app/src/test/java/nl/tudelft/trustchain/app/ExampleUnitTest.kt +++ b/app/src/test/java/nl/tudelft/trustchain/app/ExampleUnitTest.kt @@ -11,6 +11,6 @@ import org.junit.Test class ExampleUnitTest { @Test fun addition_isCorrect() { - assertEquals(4, 2 + 2) + assertEquals(4, (2 + 2)) } } diff --git a/atomic-swap/src/main/java/nl/tudelft/trustchain/atomicswap/swap/Trade.kt b/atomic-swap/src/main/java/nl/tudelft/trustchain/atomicswap/swap/Trade.kt index 953f46029..e751b0c6f 100644 --- a/atomic-swap/src/main/java/nl/tudelft/trustchain/atomicswap/swap/Trade.kt +++ b/atomic-swap/src/main/java/nl/tudelft/trustchain/atomicswap/swap/Trade.kt @@ -96,6 +96,7 @@ enum class Currency(val currencyCodeStringResourceId: Int) { companion object { fun fromString(coin: String): Currency { + @Suppress("DEPRECATION") return when (coin.toLowerCase()) { "btc" -> BTC "eth" -> ETH diff --git a/build.gradle b/build.gradle index aaf041fff..72c0b27b4 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,8 @@ buildscript { ext.lifecycle_version = "2.5.1" ext.jlibtorrent_version = '1.2.17.0' ext.dokka_version = "0.10.1" + ext.hilt_ver = '2.44.2' + ext.room_version = '2.4.0' repositories { google() jcenter() diff --git a/common/build.gradle b/common/build.gradle index 8ab8b4127..95ed4bfcb 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -134,7 +134,7 @@ dependencies { testImplementation 'org.json:json:20190722' testImplementation "com.squareup.sqldelight:sqlite-driver:$sqldelight_version" testImplementation "com.goterl:lazysodium-java:5.1.4" - annotationProcessor 'androidx.room:room-compiler:2.2.5' + annotationProcessor "androidx.room:room-compiler:$room_version" } diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/CoinCommunity.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/CoinCommunity.kt index c589054ec..7de16ee88 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/CoinCommunity.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/CoinCommunity.kt @@ -15,8 +15,8 @@ import nl.tudelft.trustchain.currencyii.util.DAOJoinHelper import nl.tudelft.trustchain.currencyii.util.DAOTransferFundsHelper @Suppress("UNCHECKED_CAST") -class CoinCommunity : Community() { - override val serviceId = "02313685c1912a141279f8248fc8db5899c5df5b" +class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc8db5899c5df5b") : Community() { + override val serviceId = serviceId private fun getTrustChainCommunity(): TrustChainCommunity { return IPv8Android.getInstance().getOverlay() @@ -73,16 +73,14 @@ class CoinCommunity : Community() { walletBlockData: TrustChainTransaction, blockData: SWSignatureAskBlockTD, responses: List, - context: Context, - activity: Activity + context: Context ) { daoJoinHelper.joinBitcoinWallet( myPeer, walletBlockData, blockData, responses, - context, - activity + context ) } diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/coin/WalletManager.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/coin/WalletManager.kt index e9840a702..52dfa7bbf 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/coin/WalletManager.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/coin/WalletManager.kt @@ -8,8 +8,6 @@ import nl.tudelft.ipv8.util.toHex import nl.tudelft.trustchain.currencyii.CoinCommunity.Companion.DEFAULT_BITCOIN_MAX_TIMEOUT import nl.tudelft.trustchain.currencyii.util.taproot.* import org.bitcoinj.core.* -import org.bitcoinj.core.DumpedPrivateKey -import org.bitcoinj.core.LegacyAddress import org.bitcoinj.core.listeners.DownloadProgressTracker import org.bitcoinj.crypto.DeterministicKey import org.bitcoinj.kits.WalletAppKit @@ -68,6 +66,12 @@ class WalletManager( var progress: Int = 0 val key = addressPrivateKeyPair + val onSetupCompletedListeners = mutableListOf<() -> Unit>() + + fun addOnSetupCompletedListener(listener: () -> Unit) { + onSetupCompletedListeners.add(listener) + } + /** * Initializes WalletManager. */ @@ -95,6 +99,10 @@ class WalletManager( } wallet().allowSpendingUnconfirmedTransactions() Log.i("Coin", "Coin: WalletManager started successfully.") + onSetupCompletedListeners.forEach { + Log.i("Coin", "Coin: calling listener $it") + it() + } } } @@ -246,7 +254,8 @@ class WalletManager( val transaction = Transaction(params) transaction.addOutput( - entranceFee, Address.fromString(params, addressMuSig) + entranceFee, + Address.fromString(params, addressMuSig) ) // no fees since we are in a test network and this is a proof of concept still @@ -259,7 +268,10 @@ class WalletManager( kit.wallet().completeTx(req) Log.i("Coin", "SafeCreationAndSendGensisWallet - txid: " + req.tx.txId.toString()) - Log.i("Coin", "SafeCreationAndSendGensisWallet - serialized tx: " + req.tx.bitcoinSerialize().toHex()) + Log.i( + "Coin", + "SafeCreationAndSendGensisWallet - serialized tx: " + req.tx.bitcoinSerialize().toHex() + ) val serializedTransaction = req.tx.bitcoinSerialize() @@ -326,7 +338,11 @@ class WalletManager( kit.wallet().signTransaction(req) Log.i("Coin", "Joining DAO - newtxid: " + newTransaction.txId.toString()) - Log.i("Coin", "Joining DAO - serialized new tx without signatures: " + newTransaction.bitcoinSerialize().toHex()) + Log.i( + "Coin", + "Joining DAO - serialized new tx without signatures: " + newTransaction.bitcoinSerialize() + .toHex() + ) // TODO there is probably a bug if multiple vins are required by our own wallet (for example, multiple small txin's combined to 1 big vout) return req.tx.bitcoinSerialize().toHex() @@ -376,7 +392,10 @@ class WalletManager( sighashMuSig ) - Log.i("NONCE_KEY", "nonce_key priv: " + getNonceKey(walletId, context).first.privKey.toString()) + Log.i( + "NONCE_KEY", + "nonce_key priv: " + getNonceKey(walletId, context).first.privKey.toString() + ) return signature } @@ -415,7 +434,10 @@ class WalletManager( newTransaction.wit = cTxWitness - Log.i("Coin", "Joining DAO - serialized new tx with signatures: " + newTransaction.serialize().toHex()) + Log.i( + "Coin", + "Joining DAO - serialized new tx with signatures: " + newTransaction.serialize().toHex() + ) return Pair(sendTaprootTransaction(newTransaction), newTransaction.serialize().toHex()) } @@ -456,7 +478,11 @@ class WalletManager( receiverAddress ) - Log.i("Coin", "Transfer funds DAO - serialized new tx without signature: " + newTransaction.serialize().toHex()) + Log.i( + "Coin", + "Transfer funds DAO - serialized new tx without signature: " + newTransaction.serialize() + .toHex() + ) val privChallenge = detKey.privKey.multiply(BigInteger(1, cMap[key.decompress()])).mod(Schnorr.n) @@ -517,7 +543,11 @@ class WalletManager( newTransaction.wit = cTxWitness - Log.i("Coin", "Transfer funds DAO - final serialized new tx with signature: " + newTransaction.serialize().toHex()) + Log.i( + "Coin", + "Transfer funds DAO - final serialized new tx with signature: " + newTransaction.serialize() + .toHex() + ) return Pair(sendTaprootTransaction(newTransaction), newTransaction.serialize().toHex()) } diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/ui/bitcoin/JoinDAOFragment.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/ui/bitcoin/JoinDAOFragment.kt index ad03e645e..16bd4b1bf 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/ui/bitcoin/JoinDAOFragment.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/ui/bitcoin/JoinDAOFragment.kt @@ -8,7 +8,6 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import kotlinx.android.synthetic.main.fragment_join_network.* -import kotlinx.android.synthetic.main.fragment_shared_wallet_transaction.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -205,7 +204,6 @@ class JoinDAOFragment : BaseFragment(R.layout.fragment_join_network) { } val context = requireContext() - val activityRequired = requireActivity() // Wait and collect signatures var signatures: List? = null while (signatures == null) { @@ -220,11 +218,11 @@ class JoinDAOFragment : BaseFragment(R.layout.fragment_join_network) { mostRecentSWBlock.transaction, proposeBlockData, signatures, - context, - activityRequired + context ) // Add new nonceKey after joining a DAO - WalletManagerAndroid.getInstance().addNewNonceKey(proposeBlockData.SW_UNIQUE_ID, context) + WalletManagerAndroid.getInstance() + .addNewNonceKey(proposeBlockData.SW_UNIQUE_ID, context) } catch (t: Throwable) { Log.e("Coin", "Joining failed. ${t.message ?: "No further information"}.") setAlertText(t.message ?: "Unexpected error occurred. Try again") diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOJoinHelper.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOJoinHelper.kt index a65669b4b..ac482a6f0 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOJoinHelper.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOJoinHelper.kt @@ -1,9 +1,7 @@ package nl.tudelft.trustchain.currencyii.util -import android.app.Activity import android.content.Context import android.util.Log -import android.widget.Toast import nl.tudelft.ipv8.Peer import nl.tudelft.ipv8.android.IPv8Android import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock @@ -128,8 +126,7 @@ class DAOJoinHelper { walletBlockData: TrustChainTransaction, blockData: SWSignatureAskBlockTD, responses: List, - context: Context, - activity: Activity + context: Context ) { val oldWalletBlockData = SWJoinBlockTransactionData(walletBlockData) val newTransactionSerialized = blockData.SW_TRANSACTION_SERIALIZED @@ -157,14 +154,8 @@ class DAOJoinHelper { if (status) { Log.i("Coin", "Successfully submitted taproot transaction to server") - activity.runOnUiThread { - Toast.makeText(context, "Successfully submitted the transaction", Toast.LENGTH_SHORT).show() - } } else { Log.e("Coin", "Taproot transaction submission to server failed") - activity.runOnUiThread { - Toast.makeText(context, "Failed to submit the transaction to the server", Toast.LENGTH_SHORT).show() - } } oldWalletBlockData.getData().SW_NONCE_PKS = newNonces @@ -187,7 +178,12 @@ class DAOJoinHelper { newData.addBitcoinPk(walletManager.networkPublicECKeyHex()) newData.addTrustChainPk(myPeer.publicKey.keyToBin().toHex()) newData.setTransactionSerialized(serializedTransaction) - newData.addNoncePk(walletManager.addNewNonceKey(oldBlockData.getData().SW_UNIQUE_ID, context).second.getEncoded(true).toHex()) + newData.addNoncePk( + walletManager.addNewNonceKey( + oldBlockData.getData().SW_UNIQUE_ID, + context + ).second.getEncoded(true).toHex() + ) trustchain.createProposalBlock( newData.getJsonString(), @@ -212,9 +208,16 @@ class DAOJoinHelper { val trustchain = TrustChainHelper(IPv8Android.getInstance().getOverlay() ?: return) val blockData = SWSignatureAskTransactionData(block.transaction).getData() - Log.i("Coin", "Signature request for joining: ${blockData.SW_RECEIVER_PK}, me: ${myPublicKey.toHex()}") + Log.i( + "Coin", + "Signature request for joining: ${blockData.SW_RECEIVER_PK}, me: ${myPublicKey.toHex()}" + ) if (blockData.SW_RECEIVER_PK != myPublicKey.toHex()) { + Log.i( + "Coin", + "${blockData.SW_RECEIVER_PK} != ${myPublicKey.toHex()}" + ) return } Log.i("Coin", "Signing join block transaction: $blockData") diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOTransferFundsHelper.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOTransferFundsHelper.kt index 632a6c4f2..83722dfbe 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOTransferFundsHelper.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/DAOTransferFundsHelper.kt @@ -132,12 +132,12 @@ class DAOTransferFundsHelper { ) if (status) { - Log.i("Coin", "successfully submitted taproot transaction to server") + Log.d("MVDAO", "successfully submitted taproot transaction to server") activity.runOnUiThread { Toast.makeText(context, "Successfully submitted the transaction", Toast.LENGTH_SHORT).show() } } else { - Log.e("Coin", "taproot transaction submission to server failed") + Log.d("MVDAO", "taproot transaction submission to server failed") activity.runOnUiThread { Toast.makeText(context, "Failed to submit the transaction to the server", Toast.LENGTH_SHORT).show() } diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/CTransaction.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/CTransaction.kt index c0d2c6f90..f54d1d4fa 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/CTransaction.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/CTransaction.kt @@ -67,6 +67,7 @@ class CTransaction( vin = deserializeVectorCTxIn(bytes) var flags: Char = 0.toChar() if (vin.isEmpty()) { + @Suppress("DEPRECATION") flags = ByteBuffer.wrap(read(bytes, 1)).order(ByteOrder.LITTLE_ENDIAN).get().toChar() if (flags != 0.toChar()) { vin = deserializeVectorCTxIn(bytes) @@ -469,6 +470,7 @@ fun littleEndian(long: Long): ByteArray { fun littleEndian(char: Char): ByteArray { val bb: ByteBuffer = ByteBuffer.allocate(1) bb.order(ByteOrder.LITTLE_ENDIAN) + @Suppress("DEPRECATION") bb.put(char.toByte()) return bb.array() } diff --git a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/Messages.kt b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/Messages.kt index e1a72db66..436627185 100644 --- a/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/Messages.kt +++ b/currencyii/src/main/java/nl/tudelft/trustchain/currencyii/util/taproot/Messages.kt @@ -6,6 +6,7 @@ import java.nio.ByteOrder class Messages { + @Suppress("DEPRECATION") companion object { fun serCompactSize(l: Int): ByteArray { var ssBuf: ByteArray = byteArrayOf() diff --git a/debug/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt b/debug/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt index 6c3b3f2a4..487585659 100644 --- a/debug/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt +++ b/debug/src/test/java/nl/tudelft/trustchain/debug/ExampleUnitTest.kt @@ -11,6 +11,6 @@ import org.junit.Test class ExampleUnitTest { @Test fun addition_isCorrect() { - assertEquals(4, 2 + 2) + assertEquals(4, (2 + 2)) } } diff --git a/distributedai/src/test/java/com/example/dna/ExampleUnitTest.kt b/distributedai/src/test/java/com/example/dna/ExampleUnitTest.kt index ad4a46063..020ecdd58 100644 --- a/distributedai/src/test/java/com/example/dna/ExampleUnitTest.kt +++ b/distributedai/src/test/java/com/example/dna/ExampleUnitTest.kt @@ -11,6 +11,6 @@ import org.junit.Test class ExampleUnitTest { @Test fun addition_isCorrect() { - assertEquals(4, 2 + 2) + assertEquals(4, (2 + 2)) } } diff --git a/eurotoken/build.gradle b/eurotoken/build.gradle index d66454a43..b4995df43 100644 --- a/eurotoken/build.gradle +++ b/eurotoken/build.gradle @@ -55,7 +55,7 @@ dependencies { implementation 'androidx.navigation:navigation-ui-ktx:2.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'androidx.room:room-runtime:2.2.2' + implementation "androidx.room:room-runtime:$room_version" implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" @@ -82,7 +82,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - annotationProcessor 'androidx.room:room-compiler:2.2.2' + annotationProcessor "androidx.room:room-compiler:$room_version" } diff --git a/eurotoken/src/test/java/nl/tudelft/trustchain/eurotoken/ExampleUnitTest.kt b/eurotoken/src/test/java/nl/tudelft/trustchain/eurotoken/ExampleUnitTest.kt index 9103b473f..940685fa2 100644 --- a/eurotoken/src/test/java/nl/tudelft/trustchain/eurotoken/ExampleUnitTest.kt +++ b/eurotoken/src/test/java/nl/tudelft/trustchain/eurotoken/ExampleUnitTest.kt @@ -11,6 +11,6 @@ import org.junit.Test class ExampleUnitTest { @Test fun addition_isCorrect() { - assertEquals(4, 2 + 2) + assertEquals(4, (2 + 2)) } } diff --git a/freedomOfComputing/src/test/java/nl/tudelft/trustchain/FOC/ExampleUnitTest.kt b/freedomOfComputing/src/test/java/nl/tudelft/trustchain/FOC/ExampleUnitTest.kt index 3969f3f0a..2d64b0672 100644 --- a/freedomOfComputing/src/test/java/nl/tudelft/trustchain/FOC/ExampleUnitTest.kt +++ b/freedomOfComputing/src/test/java/nl/tudelft/trustchain/FOC/ExampleUnitTest.kt @@ -11,6 +11,6 @@ import org.junit.Test class ExampleUnitTest { @Test fun addition_isCorrect() { - assertEquals(4, 2 + 2) + assertEquals(4, (2 + 2)) } } diff --git a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt index ccbc80708..10e947630 100644 --- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt +++ b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/RecommenderCommunity.kt @@ -166,6 +166,7 @@ open class RecommenderCommunity( 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") @@ -239,6 +240,7 @@ open class RecommenderCommunity( 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") @@ -255,6 +257,7 @@ open class RecommenderCommunity( 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( 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 index 872b2aaed..e57feae6b 100644 --- a/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/db/RecommenderStore.kt +++ b/gossipML/src/main/java/nl/tudelft/trustchain/gossipML/db/RecommenderStore.kt @@ -2,7 +2,7 @@ package nl.tudelft.trustchain.gossipML.db import android.annotation.SuppressLint import android.util.Log -import com.example.musicdao_datafeeder.AudioFileFilter +import nl.tudelft.trustchain.musicdaodatafeeder.AudioFileFilter import com.mpatric.mp3agic.Mp3File import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job diff --git a/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/FireMissilesDialog.kt b/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/FireMissilesDialog.kt index b0cb3797b..9e1469f19 100644 --- a/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/FireMissilesDialog.kt +++ b/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/FireMissilesDialog.kt @@ -76,6 +76,7 @@ class FireMissilesDialog(private val peer: Peer) : DialogFragment() { } else { attestationCommunity.requestAttestation( peer, + @Suppress("DEPRECATION") attrInput.text.toString().toUpperCase(Locale.getDefault()), key, hashMapOf("id_format" to idFormat), diff --git a/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/PresentAttestationDialog.kt b/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/PresentAttestationDialog.kt index 8db560b1e..6c6b42ede 100644 --- a/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/PresentAttestationDialog.kt +++ b/ig-ssi/src/main/java/nl/tudelft/trustchain/ssi/dialogs/PresentAttestationDialog.kt @@ -33,6 +33,7 @@ class PresentAttestationDialog( builder.setView(mView) val dialog: Dialog + @Suppress("DEPRECATION") val title = "Attestation for ${attributeName.capitalize()}" val message = "$attributeName: $attributeValue" if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { diff --git a/musicdao-datafeeder/build.gradle b/musicdao-datafeeder/build.gradle index 0f9a6c3a4..2e8370dbe 100644 --- a/musicdao-datafeeder/build.gradle +++ b/musicdao-datafeeder/build.gradle @@ -25,7 +25,7 @@ dependencies { } application { - mainClassName = 'com.example.musicdao_datafeeder.DataFeederKt' + mainClassName = 'nl.trustchain.tudelft.musicdaodatafeeder.DataFeederKt' } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/AudioFileFilter.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/AudioFileFilter.kt similarity index 84% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/AudioFileFilter.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/AudioFileFilter.kt index bb6128222..141e46566 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/AudioFileFilter.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/AudioFileFilter.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import java.io.File import java.io.FilenameFilter diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/CryptoCurrencyConfig.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/CryptoCurrencyConfig.kt similarity index 80% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/CryptoCurrencyConfig.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/CryptoCurrencyConfig.kt index 9a913930a..0101616da 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/CryptoCurrencyConfig.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/CryptoCurrencyConfig.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import org.bitcoinj.params.RegTestParams diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataFeeder.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataFeeder.kt similarity index 99% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataFeeder.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataFeeder.kt index 3407e5b16..99d7e209e 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataFeeder.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataFeeder.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import com.frostwire.jlibtorrent.TorrentInfo import com.mpatric.mp3agic.Mp3File diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataPropagator.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataPropagator.kt similarity index 99% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataPropagator.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataPropagator.kt index 4e3681ddb..be7974081 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/DataPropagator.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/DataPropagator.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import com.frostwire.jlibtorrent.TorrentInfo import com.mpatric.mp3agic.Mp3File diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/KeywordSearchMessage.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/KeywordSearchMessage.kt similarity index 96% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/KeywordSearchMessage.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/KeywordSearchMessage.kt index f5bb754fc..06b429b85 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/KeywordSearchMessage.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/KeywordSearchMessage.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import nl.tudelft.ipv8.messaging.* diff --git a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/MusicCommunity.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/MusicCommunity.kt similarity index 97% rename from musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/MusicCommunity.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/MusicCommunity.kt index f61e23a0b..f4bc55152 100644 --- a/musicdao-datafeeder/src/main/java/com/example/musicdao_datafeeder/MusicCommunity.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/MusicCommunity.kt @@ -1,4 +1,4 @@ -package com.example.musicdao_datafeeder +package nl.tudelft.trustchain.musicdaodatafeeder import com.frostwire.jlibtorrent.Sha1Hash import nl.tudelft.ipv8.Overlay @@ -60,6 +60,7 @@ class MusicCommunity( * blocks to find whether I have something. If I do, send the corresponding block directly back * to the original asker. If I don't, I will ask my peers to find it */ + @Suppress("DEPRECATION") private fun onKeywordSearch(packet: Packet) { val (peer, payload) = packet.getAuthPayload(KeywordSearchMessage) val keyword = payload.keyword.toLowerCase(Locale.ROOT) @@ -93,6 +94,7 @@ class MusicCommunity( * Filter local databse to find a release block that matches a certain title or artist, using * keyword search */ + @Suppress("DEPRECATION") fun localKeywordSearch(keyword: String): TrustChainBlock? { database.getBlocksWithType("publish_release").forEach { val transaction = it.transaction diff --git a/musicdao/src/main/java/com/example/musicdao/ipv8/SwarmHealth.kt b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/SwarmHealth.kt similarity index 98% rename from musicdao/src/main/java/com/example/musicdao/ipv8/SwarmHealth.kt rename to musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/SwarmHealth.kt index 41263204a..705bc6527 100644 --- a/musicdao/src/main/java/com/example/musicdao/ipv8/SwarmHealth.kt +++ b/musicdao-datafeeder/src/main/java/nl/tudelft/trustchain/musicdaodatafeeder/SwarmHealth.kt @@ -1,4 +1,4 @@ -package com.example.musicdao.ipv8 +package nl.tudelft.trustchain.musicdaodatafeeder import nl.tudelft.ipv8.messaging.* import java.util.* diff --git a/musicdao/build.gradle b/musicdao/build.gradle index 19cd03034..7266d6439 100644 --- a/musicdao/build.gradle +++ b/musicdao/build.gradle @@ -2,7 +2,24 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'org.jlleitschuh.gradle.ktlint' -apply plugin: 'androidx.navigation.safeargs.kotlin' +apply plugin: 'androidx.navigation.safeargs' +apply plugin: 'kotlin-kapt' +apply plugin: "dagger.hilt.android.plugin" + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.google.dagger:hilt-android-gradle-plugin:$hilt_ver") + } +} + +configurations.all { + resolutionStrategy { force 'androidx.core:core-ktx:1.7.0-alpha01' } +} android { compileSdkVersion 33 @@ -12,6 +29,7 @@ android { buildFeatures { viewBinding = true + compose true } defaultConfig { @@ -43,19 +61,27 @@ android { 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'] } } - namespace 'com.example.musicdao' + composeOptions { + kotlinCompilerVersion kotlin_version + kotlinCompilerExtensionVersion "1.3.1" + } + + namespace 'nl.tudelft.trustchain.musicdao' } dependencies { implementation project(':gossipML') implementation project(':common') - api(project(':ipv8-android')){ + implementation project(':currencyii') + + api(project(':ipv8-android')) { exclude group: 'net.java.dev.jna' exclude group: 'org.bouncycastle' } @@ -86,10 +112,10 @@ dependencies { // 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' + implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-ui:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' // Cryptocurrency integration implementation 'org.bitcoinj:bitcoinj-core:0.15.10' @@ -114,6 +140,42 @@ dependencies { // Logging implementation 'io.github.microutils:kotlin-logging:1.7.7' + + // Integration with activities + implementation 'androidx.activity:activity-compose:1.3.1' + // Compose Material Design + implementation "androidx.compose.material:material:1.0.1" + // Animations + implementation "androidx.compose.animation:animation:1.0.1" + // Tooling support (Previews, etc.) + implementation 'androidx.compose.ui:ui-tooling:1.0.1' + // Integration with ViewModels + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07' + // UI Tests + androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.1' + // Compose + // Runtime + implementation "androidx.compose.runtime:runtime-livedata:1.0.1" + + implementation("com.google.accompanist:accompanist-swiperefresh:0.17.0") + implementation "com.google.accompanist:accompanist-navigation-animation:0.29.1-alpha" + + // Room + implementation "androidx.room:room-runtime:$room_version" + implementation("androidx.room:room-ktx:$room_version") + annotationProcessor "androidx.room:room-compiler:$room_version" + kapt("androidx.room:room-compiler:$room_version") + + // Hilt + implementation("com.google.dagger:hilt-android:$hilt_ver") + implementation("androidx.hilt:hilt-navigation-fragment:1.0.0-alpha03") + implementation("androidx.hilt:hilt-navigation-compose:1.0.0-alpha03") + kapt("com.google.dagger:hilt-android-compiler:$hilt_ver") + + // GSON + implementation("com.google.code.gson:gson:2.8.9") + implementation("org.apache.commons:commons-csv:1.9.0") + } repositories { @@ -121,11 +183,10 @@ repositories { } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions.freeCompilerArgs += [ - "-opt-in=kotlin.Experimental,kotlin.ExperimentalUnsignedTypes", - ] + kotlinOptions.freeCompilerArgs += ["-opt-in=kotlin.Experimental,kotlin.ExperimentalUnsignedTypes",] } + tasks.withType(Test) { systemProperty "java.library.path", "../common/libs" } diff --git a/musicdao/src/androidTest/java/com/example/musicdao/ExampleInstrumentedTest.kt b/musicdao/src/androidTest/java/com/example/musicdao/ExampleInstrumentedTest.kt deleted file mode 100644 index 2e1a336a3..000000000 --- a/musicdao/src/androidTest/java/com/example/musicdao/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.example.musicdao - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.example.musicdao.test", appContext.packageName) - } -} diff --git a/musicdao/src/main/AndroidManifest.xml b/musicdao/src/main/AndroidManifest.xml index 7e388d8df..7dc90db3c 100644 --- a/musicdao/src/main/AndroidManifest.xml +++ b/musicdao/src/main/AndroidManifest.xml @@ -2,4 +2,6 @@ + + diff --git a/musicdao/src/main/java/com/example/musicdao/MusicBaseFragment.kt b/musicdao/src/main/java/com/example/musicdao/MusicBaseFragment.kt deleted file mode 100644 index a53855138..000000000 --- a/musicdao/src/main/java/com/example/musicdao/MusicBaseFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.example.musicdao - -import androidx.annotation.LayoutRes -import com.example.musicdao.ipv8.MusicCommunity -import nl.tudelft.trustchain.common.ui.BaseFragment -import nl.tudelft.trustchain.gossipML.RecommenderCommunity - -/** - * An abstract layer between the MusicCommunity and an Android Fragment - */ -abstract class MusicBaseFragment(@LayoutRes contentLayoutId: Int = 0) : - BaseFragment(contentLayoutId) { - protected fun getMusicCommunity(): MusicCommunity { - return getIpv8().getOverlay() - ?: throw IllegalStateException("MusicCommunity is not configured") - } - protected fun getRecommenderCommunity(): RecommenderCommunity { - return getIpv8().getOverlay() - ?: throw IllegalStateException("RecommenderCommunity is not configured") - } -} diff --git a/musicdao/src/main/java/com/example/musicdao/MusicService.kt b/musicdao/src/main/java/com/example/musicdao/MusicService.kt deleted file mode 100644 index 9ef65008e..000000000 --- a/musicdao/src/main/java/com/example/musicdao/MusicService.kt +++ /dev/null @@ -1,271 +0,0 @@ -package com.example.musicdao - -import android.app.SearchManager -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.content.res.Resources -import android.net.Uri -import android.os.Bundle -import android.os.IBinder -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope -import androidx.navigation.findNavController -import com.example.musicdao.ipv8.MusicCommunity -import com.example.musicdao.ipv8.SwarmHealth -import com.example.musicdao.net.ContentSeeder -import com.example.musicdao.player.AudioPlayer -import com.example.musicdao.util.ReleaseFactory -import com.example.musicdao.util.Util -import com.example.musicdao.wallet.WalletService -import com.frostwire.jlibtorrent.* -import kotlinx.android.synthetic.main.dialog_tip_artist.* -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import nl.tudelft.ipv8.android.IPv8Android -import nl.tudelft.ipv8.attestation.trustchain.BlockSigner -import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock -import java.io.File -import kotlin.random.Random - -/** - * This maintains the interactions between the UI and seeding/trustchain - */ -class MusicService : AppCompatActivity() { - // Popularity measurement by swarm health - var swarmHealthMap = mutableMapOf() - private lateinit var musicGossipingService: MusicGossipingService - private var mBound = false // Whether the musicGossipingService is bound to the current activity - private val navigationGraph: Int = R.navigation.musicdao_navgraph - - private val connection = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, service: IBinder) { - // We've bound to LocalService, cast the IBinder and get LocalService instance - val binder = service as MusicGossipingService.LocalBinder - musicGossipingService = binder.getService() - mBound = true - } - - override fun onServiceDisconnected(arg0: ComponentName) { - mBound = false - } - } - - var sessionManager: SessionManager? = null - - private val navController by lazy { - findNavController(R.id.navHostFragment) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.fragment_base) - navController.setGraph(navigationGraph) - handleIntent(intent) - actionBar?.setDisplayHomeAsUpEnabled(true) - - startup() - } - - override fun onDestroy() { - AudioPlayer.getInstance()?.exoPlayer?.stop() - sessionManager?.stop() - sessionManager = null - super.onDestroy() - } - - override fun onStart() { - super.onStart() - - Intent(this, MusicGossipingService::class.java).also { intent -> - startService(intent) - bindService(intent, connection, Context.BIND_AUTO_CREATE) - } - } - - override fun onStop() { - super.onStop() - - if (mBound) { - unbindService(connection) - mBound = false - } - } - - private fun startup() { - val ses = SessionManager() - ses.start() - registerBlockSigner() - iterativelyUpdateSwarmHealth() - - // Start ContentSeeder service: for serving music torrents to other devices - ContentSeeder.getInstance( - applicationContext.cacheDir, - ses - ).start() - - // Start WalletService, for maintaining and sending coins - WalletService.getInstance(applicationContext.cacheDir, this@MusicService) - .start() - - sessionManager = ses - } - - override fun onNewIntent(intent: Intent) { - super.onNewIntent(intent) - setIntent(intent) - handleIntent(intent) - } - - private fun handleIntent(intent: Intent) { - if (Intent.ACTION_SEARCH == intent.action) { - intent.getStringExtra(SearchManager.QUERY)?.also { query -> - val args = Bundle() - args.putString("filter", query) - findNavController(R.id.navHostFragment).navigate( - R.id.playlistsOverviewFragment, - args - ) - } - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - if (menu == null) return false - val inflater: MenuInflater = menuInflater - inflater.inflate(R.menu.menu_searchable, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - onBackPressed() - return true - } - R.id.action_search -> { - onSearchRequested() - true - } - else -> super.onOptionsItemSelected(item) - } - } - - /** - * Keep track of Swarm Health for all torrents being monitored - */ - private fun iterativelyUpdateSwarmHealth() { - lifecycleScope.launchWhenStarted { - while (isActive) { - swarmHealthMap = filterSwarmHealthMap() - - if (mBound) { - musicGossipingService.setSwarmHealthMap(swarmHealthMap) - } - delay(3000) - } - } - } - - /** - * Merge local and remote swarm health map and remove outdated data - */ - private fun filterSwarmHealthMap(): MutableMap { - val localMap = updateLocalSwarmHealthMap() - val musicCommunity = IPv8Android.getInstance().getOverlay() - val communityMap = musicCommunity?.swarmHealthMap ?: mutableMapOf() - // Keep the highest numPeers/numSeeds count of all items in both maps - // This map contains all the combined data, where local and community map data are merged; - // the highest connectivity count for each item is saved in a gloal map for the MusicService - val map: MutableMap = mutableMapOf() - val allKeys = localMap.keys + communityMap.keys - for (infoHash in allKeys) { - val shLocal = localMap[infoHash] - val shRemote = communityMap[infoHash] - - val bestSwarmHealth = SwarmHealth.pickBest(shLocal, shRemote) - if (bestSwarmHealth != null) { - map[infoHash] = bestSwarmHealth - } - } - // Remove outdated swarm health data: if the data is outdated, we throw it away - return map.filterValues { it.isUpToDate() }.toMutableMap() - } - - /** - * Go through all the torrents that we are currently seeding and mark its connectivity to peers - */ - private fun updateLocalSwarmHealthMap(): MutableMap { - val sessionManager = sessionManager ?: return mutableMapOf() - val contentSeeder = - ContentSeeder.getInstance(cacheDir, sessionManager) - val localMap = contentSeeder.swarmHealthMap - for (infoHash in localMap.keys) { - // Update all connectivity stats of the torrents that we are currently seeding - if (sessionManager.isRunning) { - val handle = sessionManager.find(infoHash) ?: continue - val newSwarmHealth = SwarmHealth( - infoHash.toString(), - handle.status().numPeers().toUInt(), - handle.status().numSeeds().toUInt() - ) - // Never go below 1, because we know we are at least 1 seeder of our local files - if (newSwarmHealth.numSeeds.toInt() < 1) continue - localMap[infoHash] = newSwarmHealth - } - } - return localMap - } - - /** - * Show libtorrent connectivity stats - */ - fun getStatsOverview(): String { - val sessionManager = sessionManager ?: return "Starting torrent client..." - if (!sessionManager.isRunning) return "Starting torrent client..." - return "up: ${Util.readableBytes(sessionManager.uploadRate())}, down: ${ - Util.readableBytes(sessionManager.downloadRate()) - }, dht nodes (torrent): ${sessionManager.dhtNodes()}" - } - - /** - * On discovering a half block, with tag publish_release, agree it immediately (for now). In the - * future there will be logic added here to determine whether an upload was done by the correct - * artist/label (artist passport). - */ - private fun registerBlockSigner() { - val musicCommunity = IPv8Android.getInstance().getOverlay() - musicCommunity?.registerBlockSigner( - "publish_release", - object : BlockSigner { - override fun onSignatureRequest(block: TrustChainBlock) { - musicCommunity.createAgreementBlock(block, mapOf()) - } - } - ) - } - - /** - * Generates a a .torrent File from local files - * @param uris the list of Uris pointing to local audio source files to publish - */ - @Throws(Resources.NotFoundException::class) - fun generateTorrent(context: Context, uris: List): File { - val contentResolver = context.contentResolver - val randomInt = Random.nextInt(0, Int.MAX_VALUE) - val parentDir = "${context.cacheDir}/$randomInt" - - return ReleaseFactory.generateTorrent(parentDir, uris, contentResolver) - } - - fun showToast(text: String, length: Int) { - runOnUiThread { - Toast.makeText(baseContext, text, length).show() - } - } -} diff --git a/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistCoverFragment.kt b/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistCoverFragment.kt deleted file mode 100644 index 83b8ac224..000000000 --- a/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistCoverFragment.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.example.musicdao.catalog - -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import com.example.musicdao.R -import kotlinx.android.synthetic.main.fragment_release_cover.* -import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock -import java.io.File -import java.util.* - -/** - * An 'album cover' or other visual display of a playlist, that can be clicked to view its contents - */ -class PlaylistCoverFragment( - private val trustChainBlock: TrustChainBlock, - private val connectivity: Int = 0, - private val coverArt: File? = null -) : - Fragment(R.layout.fragment_release_cover) { - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val transaction = trustChainBlock.transaction - val publisher = transaction["publisher"].toString() - val magnet = transaction["magnet"].toString() - val title = transaction["title"].toString() - val artists = transaction["artists"].toString() - val date = transaction["date"].toString() - var torrentInfoName = "" - if (transaction.containsKey("torrentInfoName")) { - torrentInfoName = transaction["torrentInfoName"].toString() - } - - coverTitle.text = title - coverArtists.text = artists - seedCount.text = "Peers: $connectivity" - if (coverArt != null) { - coverArtImage.setImageURI(Uri.fromFile(coverArt)) - } - - coverCard.setOnClickListener { - val action = - PlaylistsOverviewFragmentDirections.actionPlaylistsOverviewFragmentToPlaylistFragment( - publisher, - magnet, - title, - artists, - date, - torrentInfoName - ) - findNavController().navigate(action) - } - } - - /** - * Filter out the content by a simple string keyword - */ - fun filter(name: String): Boolean { - if (name.isEmpty()) return true - val query = name.toLowerCase(Locale.ROOT) - val transaction = trustChainBlock.transaction - val title = transaction["title"].toString().toLowerCase(Locale.ROOT) - val artists = transaction["artists"].toString().toLowerCase(Locale.ROOT) - val date = transaction["date"].toString().toLowerCase(Locale.ROOT) - if (title.contains(query)) { - return true - } - if (artists.contains(query)) { - return true - } - if (date.contains(query)) { - return true - } - return false - } -} diff --git a/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistsOverviewFragment.kt b/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistsOverviewFragment.kt deleted file mode 100644 index 7f8ab4616..000000000 --- a/musicdao/src/main/java/com/example/musicdao/catalog/PlaylistsOverviewFragment.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.example.musicdao.catalog - -import android.os.Bundle -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import com.example.musicdao.MusicBaseFragment -import com.example.musicdao.MusicService -import com.example.musicdao.R -import com.example.musicdao.dialog.SubmitReleaseDialog -import com.example.musicdao.util.Util -import com.example.musicdao.wallet.WalletService -import com.frostwire.jlibtorrent.Sha1Hash -import kotlinx.android.synthetic.main.fragment_release_overview.* -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import nl.tudelft.ipv8.android.IPv8Android -import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock -import java.io.File - -/** - * A screen showing an overview of playlists to browse through - */ -class PlaylistsOverviewFragment : MusicBaseFragment(R.layout.fragment_release_overview) { - private var releaseRefreshCount = 0 - private var lastReleaseBlocksSize = -1 - private var lastSwarmHealthMapSize = -1 - private var searchQuery = "" - private val maxPlaylists = 100 // Max playlists to show - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - searchQuery = arguments?.getString("filter", "") ?: "" - if (searchQuery != "") { - getMusicCommunity().performRemoteKeywordSearch(searchQuery) - activity?.title = "Search results" - setMenuVisibility(false) - setHasOptionsMenu(false) - } else { - setHasOptionsMenu(true) - } - - lastReleaseBlocksSize = -1 - lastSwarmHealthMapSize = -1 - releaseRefreshCount = 0 - - lifecycleScope.launchWhenCreated { - while (isActive && isAdded && !isDetached) { - if (activity is MusicService && debugText != null) { - debugText.text = (activity as MusicService).getStatsOverview() - } - if (releaseRefreshCount < 3) { - showAllReleases() - } - delay(3000) - } - } - - swipeRefresh.setOnRefreshListener { - showAllReleases() - swipeRefresh.isRefreshing = false - } - - addPlaylistFab.setOnClickListener { - showCreateReleaseDialog() - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - // Debug button is a simple toggle for a connectivity stats display - R.id.action_debug -> { - if (debugText != null) { - if (debugText.visibility == View.VISIBLE) { - debugText.visibility = View.GONE - } else { - debugText.visibility = View.VISIBLE - } - } - true - } - R.id.action_wallet -> { - findNavController().navigate(R.id.walletFragment) - true - } - else -> super.onOptionsItemSelected(item) - } - } - - /** - * List all the releases that are currently loaded in the local trustchain database. If keyword - * search is enabled (searchQuery variable is set) then it also filters the database - */ - private fun showAllReleases() { - val swarmHealthMap = (activity as MusicService).swarmHealthMap - val releaseDataMap = mutableMapOf() - val releaseBlocks = getMusicCommunity().database.getBlocksWithType("publish_release") - if (releaseBlocks.size == lastReleaseBlocksSize && - swarmHealthMap.size == lastSwarmHealthMapSize - ) { - return - } - for (block in releaseBlocks) { - // If we know the amount of seeders for the corresponding block, we add it; otherwise we - // assume the amount of seeders is 0 - var numSeeds = 0 - var numPeers = 0 - val magnet = block.transaction["magnet"] - if (magnet is String) { - val infoHash = Util.extractInfoHash(magnet) - if (infoHash is Sha1Hash) { - numSeeds = swarmHealthMap[infoHash]?.numSeeds?.toInt() ?: 0 - numPeers = swarmHealthMap[infoHash]?.numPeers?.toInt() ?: 0 - } - } - releaseDataMap[block] = numSeeds + numPeers - } - val sortedMap = releaseDataMap - .toList() - .sortedByDescending { (_, value) -> value } - .toMap() - - lastReleaseBlocksSize = releaseBlocks.size - lastSwarmHealthMapSize = swarmHealthMap.size - if (release_overview_layout is ViewGroup) { - activity?.runOnUiThread { - release_overview_layout.removeAllViews() - } - } - refreshReleaseBlocks(sortedMap) - } - - /** - * @param releaseBlocks map of: release block, number of (known) seeders - */ - fun refreshReleaseBlocks(releaseBlocks: Map): Int { - var count = 0 - for ((block, connectivity) in releaseBlocks) { - if (count == maxPlaylists) return count - val magnet = block.transaction["magnet"] - val title = block.transaction["title"] - val torrentInfoName = block.transaction["torrentInfoName"] - val publisher = block.transaction["publisher"] - if (magnet is String && magnet.length > 0 && title is String && title.length > 0 && - torrentInfoName is String && torrentInfoName.length > 0 && publisher is String && - publisher.length > 0 - ) { - val coverArt = Util.findCoverArt( - File( - context?.cacheDir?.path + "/" + Util.sanitizeString(torrentInfoName) - ) - ) - val transaction = activity?.supportFragmentManager?.beginTransaction() - val coverFragment = PlaylistCoverFragment(block, connectivity, coverArt) - if (coverFragment.filter(searchQuery)) { - transaction?.add(R.id.release_overview_layout, coverFragment, "releaseCover") - if (loadingReleases?.visibility == View.VISIBLE) { - loadingReleases.visibility = View.GONE - } - count += 1 - } - activity?.runOnUiThread { - transaction?.commitAllowingStateLoss() - } - } - } - if (count != 0) { - releaseRefreshCount += 1 - } - return count - } - - /** - * Show a form dialog which asks to add metadata for a new Release (album title, release date, - * track files etc) - */ - private fun showCreateReleaseDialog() { - SubmitReleaseDialog(this) - .show(childFragmentManager, "Submit metadata") - } - - /** - * After the user inserts some metadata for the release to be published, this function is called - * to create the proposal block - */ - fun publish( - magnet: String, - title: String, - artists: String, - releaseDate: String, - torrentInfoName: String - ) { - val myPeer = IPv8Android.getInstance().myPeer - val transaction = mutableMapOf( - "magnet" to magnet, - "title" to title, - "artists" to artists, - "date" to releaseDate, - "torrentInfoName" to torrentInfoName - ) - val walletDir = context?.cacheDir - if (walletDir != null) { - val musicWallet = WalletService.getInstance(walletDir, (activity as MusicService)) - transaction["publisher"] = musicWallet.publicKey() - } - val trustchain = getMusicCommunity() - trustchain.createProposalBlock("publish_release", transaction, myPeer.publicKey.keyToBin()) - } -} diff --git a/musicdao/src/main/java/com/example/musicdao/catalog/RecommendationFragment.kt b/musicdao/src/main/java/com/example/musicdao/catalog/RecommendationFragment.kt deleted file mode 100644 index 851a8f27d..000000000 --- a/musicdao/src/main/java/com/example/musicdao/catalog/RecommendationFragment.kt +++ /dev/null @@ -1,140 +0,0 @@ -package com.example.musicdao.catalog - -import android.os.Bundle -import android.util.Log -import android.view.View -import androidx.lifecycle.lifecycleScope -import com.example.musicdao.MusicBaseFragment -import com.example.musicdao.R -import com.example.musicdao.util.Util -import kotlinx.android.synthetic.main.fragment_recommendation.* -import kotlinx.coroutines.delay -import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock -import nl.tudelft.trustchain.gossipML.models.collaborative_filtering.MatrixFactorization -import nl.tudelft.trustchain.gossipML.models.feature_based.Pegasos -import java.io.File -import java.lang.Double.NEGATIVE_INFINITY - -class RecommendationFragment : MusicBaseFragment(R.layout.fragment_recommendation) { - private var isActive = true - private val test = false - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - Log.w("Recommend", "RecommendationFragment view created") - getRecommenderCommunity().exchangeMyFeatures() - - lifecycleScope.launchWhenCreated { - while (isActive) { - getRecommenderCommunity().initiateWalkingModel() - delay(60 * 1000) - } - } - refreshRecommend.setOnRefreshListener { - if (getRecommenderCommunity().recommendStore.globalSongCount() > 0) { - loadingRecommendations.setVisibility(View.VISIBLE) - loadingRecommendations.text = "Refreshing recommendations..." - refreshRecommendations() - val transaction = activity?.supportFragmentManager?.beginTransaction() - loadingRecommendations.setVisibility(View.GONE) - activity?.runOnUiThread { transaction?.commitAllowingStateLoss() } - refreshRecommend.isRefreshing = false - } - getRecommenderCommunity().exchangeMyFeatures() - } - } - - private fun updateRecommendFragment(block: TrustChainBlock, recNum: Int) { - Log.w("Recommend", "Adding recommendation coverFragment") - val transaction = activity?.supportFragmentManager?.beginTransaction() - - val recFrag = activity?.supportFragmentManager?.findFragmentByTag("recommendation$recNum") - if (recFrag != null) transaction?.remove(recFrag) - - val torrentName = block.transaction["torrentInfoName"] - val path = if (torrentName != null) { - context?.cacheDir?.path + "/" + Util.sanitizeString(torrentName as String) - } else { - context?.cacheDir?.path + "/" + "notfound.jpg" - } - val coverArt = Util.findCoverArt(File(path)) - val coverFragment = PlaylistCoverFragment(block, 0, coverArt) - transaction?.add(R.id.recommend_layout, coverFragment, "recommendation$recNum") - - activity?.runOnUiThread { transaction?.commitAllowingStateLoss() } - } - - /** - * Given song blocks and corresponding extracted features, make predictions with different - * feature-based models and bag them together - * - * @param data pair of features and corresponding song block - * @return array of predicted scores - */ - private fun getBaggedPredictions(data: Pair>, List>): Array { - var jointRelease = Array(data.second.size) { _ -> 0.0 } - val modelNames = arrayOf("Pegasos") - for (name in modelNames) { - Log.w("Recommend", "Getting model $name") - val colab = getRecommenderCommunity().recommendStore.getLocalModel(name) - var bestRelease = (colab as Pegasos).predict(data.first) - val sum = bestRelease.sumByDouble { it } - bestRelease = bestRelease.indices.map { bestRelease[it] / sum }.toTypedArray() - jointRelease = jointRelease.indices.map { jointRelease[it] + bestRelease[it] }.toTypedArray() - } - return jointRelease - } - - /** - * List all the releases that are currently loaded in the local trustchain database. If keyword - * search is enabled (searchQuery variable is set) then it also filters the database - */ - private fun refreshRecommendations() { - val recStore = getRecommenderCommunity().recommendStore - val data = recStore.getNewSongs() - val blocks = data.second - - try { - var colab = recStore.getLocalModel("MatrixFactorization") as MatrixFactorization - if (colab.ratings.isEmpty()) { - colab = MatrixFactorization(recStore.getSongIds().zip(recStore.getPlaycounts()).toMap().toSortedMap()) - recStore.storeModelLocally(colab) - } - val bestRelease = colab.predict() - var bestBlock = blocks[0] - for (block in blocks) { - bestBlock = block - if ("${block.transaction["title"]}-${block.transaction["artist"]}" == bestRelease) - break - } - updateRecommendFragment(bestBlock, 0) - } catch (e: Exception) { - Log.e("Recommend", "Colaborative filtering prediction failed") - Log.e("Recommend", e.toString()) - } - - try { - Log.w("Recommend", "Retrieving local recommendation model") - val predictions = getBaggedPredictions(data) - var bestScore = NEGATIVE_INFINITY - var candidateRecommendations = emptyArray() - for ((i, pred) in predictions.withIndex()) { - if (bestScore < pred) { - bestScore = pred - candidateRecommendations = arrayOf(i) - } else if (bestScore == pred) { - candidateRecommendations += i - } - } - // recommend random song out of the songs with the same scores - val best = candidateRecommendations.random() - Log.w("Recommend", "PICKED BLOCK $best with score $bestScore") - val debugScore = predictions[best].toString() - Log.w("Recommender", "After refreshing, best local score is $debugScore") - updateRecommendFragment(blocks[best], 1) - } catch (e: Exception) { - Log.e("Recommend", "Feature-based model prediction failed") - Log.e("Recommend", e.toString()) - } - } -} diff --git a/musicdao/src/main/java/com/example/musicdao/dialog/SubmitReleaseDialog.kt b/musicdao/src/main/java/com/example/musicdao/dialog/SubmitReleaseDialog.kt deleted file mode 100644 index 13c04a9f1..000000000 --- a/musicdao/src/main/java/com/example/musicdao/dialog/SubmitReleaseDialog.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.example.musicdao.dialog - -import android.app.AlertDialog -import android.app.Dialog -import android.content.DialogInterface -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.widget.Button -import android.widget.EditText -import android.widget.TextView -import android.widget.Toast -import androidx.fragment.app.DialogFragment -import com.example.musicdao.MusicService -import com.example.musicdao.R -import com.example.musicdao.catalog.PlaylistsOverviewFragment -import com.example.musicdao.util.ReleaseFactory -import com.example.musicdao.util.Util -import com.frostwire.jlibtorrent.TorrentInfo - -/** - * A form within a dialog which allows the user to submit a Release and publish it, by either - * selecting local audio files or by pasting a magnet link - */ -class SubmitReleaseDialog(private val playlistsOverviewFragment: PlaylistsOverviewFragment) : - DialogFragment() { - private var dialogView: View? = null - private var localTorrentInfo: TorrentInfo? = null - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(activity) - // Get the layout inflater - val inflater = requireActivity().layoutInflater - - dialogView = inflater.inflate(R.layout.dialog_submit_release, null) - - dialogView?.findViewById