Initial commit: Pezkuwi Wallet Android

Security hardened release:
- Code obfuscation enabled (minifyEnabled=true, shrinkResources=true)
- Sensitive files excluded (google-services.json, keystores)
- Branch.io key moved to BuildConfig placeholder
- Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77
- Comprehensive ProGuard rules for crypto wallet
- Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
2026-02-12 05:19:41 +03:00
commit a294aa1a6b
7687 changed files with 441811 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+30
View File
@@ -0,0 +1,30 @@
android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
namespace 'io.novafoundation.nova.test_shared'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation kotlinDep
implementation project(':common')
api jUnitDep
api mockitoDep
api wsDep
api gsonDep
api substrateSdkDep
api coroutinesTestDep
}
+1
View File
@@ -0,0 +1 @@
<manifest />
@@ -0,0 +1,39 @@
package io.novafoundation.nova.test_shared
import io.novasama.substrate_sdk_android.extensions.toHexString
import org.junit.Assert.assertEquals
fun <T> assertListEquals(expected: List<T>, actual: List<T>, compare: (T, T) -> Boolean = { a, b -> a == b }) {
if (expected.size != actual.size) {
throw AssertionError("Lists are not equal. Expected: $expected, actual: $actual")
}
for (i in expected.indices) {
if (!compare(expected[i], actual[i])) {
throw AssertionError("Lists are not equal. Expected: $expected, actual: $actual")
}
}
}
fun assertHexEquals(expected: ByteArray, actual: ByteArray) {
assertEquals(expected.toHexString(), actual.toHexString())
}
fun <T> assertSetEquals(expected: Set<T>, actual: Set<T>) {
if (expected != actual) {
throw AssertionError("Sets are not equal. Expected: $expected, actual: $actual")
}
}
fun <K, V> assertMapEquals(expected: Map<K, V>, actual: Map<K, V>) {
if (expected != actual) {
throw AssertionError("Maps are not equal. Expected: $expected, actual: $actual")
}
}
fun <V> assertAllItemsEquals(items: List<V>) {
items.forEach {
if (it != items[0]) {
throw AssertionError("Items in list are not equal:\n$it\n${items[0]}")
}
}
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.test_shared
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
@OptIn(ExperimentalCoroutinesApi::class)
abstract class CoroutineTest {
private val testDispatcher = TestCoroutineDispatcher()
private val testScope = TestCoroutineScope(testDispatcher)
fun runCoroutineTest(test: suspend CoroutineScope.() -> Unit) = testScope.runBlockingTest {
test(testScope)
}
}
@@ -0,0 +1,15 @@
package io.novafoundation.nova.test_shared
import io.novafoundation.nova.common.utils.CollectionDiffer
fun <T> removesElement(elementCheck: (T) -> Boolean) = argThat<CollectionDiffer.Diff<T>> {
it.newOrUpdated.isEmpty() && elementCheck(it.removed.single())
}
fun <T> insertsElement(elementCheck: (T) -> Boolean) = argThat<CollectionDiffer.Diff<T>> {
it.removed.isEmpty() && elementCheck(it.newOrUpdated.single())
}
fun <T> emptyDiff() = argThat<CollectionDiffer.Diff<T>> {
it.newOrUpdated.isEmpty() && it.removed.isEmpty()
}
@@ -0,0 +1,19 @@
package io.novafoundation.nova.test_shared
import io.novafoundation.nova.common.data.storage.encrypt.EncryptedPreferences
class HashMapEncryptedPreferences : EncryptedPreferences {
private val delegate = mutableMapOf<String, String>()
override fun putEncryptedString(field: String, value: String) {
delegate[field] = value
}
override fun getDecryptedString(field: String): String? = delegate[field]
override fun hasKey(field: String): Boolean = field in delegate
override fun removeKey(field: String) {
delegate.remove(field)
}
}
@@ -0,0 +1,23 @@
package io.novafoundation.nova.test_shared
import io.novasama.substrate_sdk_android.wsrpc.logging.Logger
object StdoutLogger : Logger {
override fun log(message: String?) {
println(message)
}
override fun log(throwable: Throwable?) {
throwable?.printStackTrace()
}
}
object NoOpLogger : Logger {
override fun log(message: String?) {
// pass
}
override fun log(throwable: Throwable?) {
// pass
}
}
@@ -0,0 +1,34 @@
package io.novafoundation.nova.test_shared
import org.mockito.ArgumentMatcher
import org.mockito.Mockito
import org.mockito.stubbing.OngoingStubbing
/**
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
* null is returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
/**
* Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
* null is returned.
*/
fun <T> any(): T = Mockito.any<T>()
/**
* Returns Mockito.isA() as nullable type to avoid java.lang.IllegalStateException when
* null is returned.
*/
fun <T> isA(classRef: Class<T>): T = Mockito.isA<T>(classRef)
fun <T> argThat(argumentMatcher: ArgumentMatcher<T>): T = Mockito.argThat<T>(argumentMatcher)
fun <T> whenever(methodCall: T): OngoingStubbing<T> =
Mockito.`when`(methodCall)
fun <T> OngoingStubbing<T>.thenThrowUnsafe(exception: Exception) = thenAnswer {
throw exception
}
@@ -0,0 +1,9 @@
package io.novafoundation.nova.test_shared
import com.google.gson.Gson
import com.neovisionaries.ws.client.WebSocketFactory
import io.novasama.substrate_sdk_android.wsrpc.SocketService
import io.novasama.substrate_sdk_android.wsrpc.recovery.Reconnector
import io.novasama.substrate_sdk_android.wsrpc.request.RequestExecutor
fun createTestSocket() = SocketService(Gson(), NoOpLogger, WebSocketFactory(), Reconnector(), RequestExecutor())