Merge remote-tracking branch 'refs/remotes/origin/master'
@@ -1,7 +1,6 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -16,17 +15,15 @@ android {
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
vectorDrawables { useSupportLibrary = true }
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -34,20 +31,10 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.4"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
kotlinOptions { jvmTarget = "1.8" }
|
||||
buildFeatures { compose = true }
|
||||
composeOptions { kotlinCompilerExtensionVersion = "1.5.4" }
|
||||
packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -59,44 +46,44 @@ dependencies {
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
|
||||
|
||||
// Accompanist for pager and animations
|
||||
implementation("com.google.accompanist:accompanist-pager:0.32.0")
|
||||
implementation("com.google.accompanist:accompanist-pager-indicators:0.32.0")
|
||||
|
||||
|
||||
// Navigation
|
||||
implementation("androidx.navigation:navigation-compose:2.7.6")
|
||||
|
||||
|
||||
// DataStore for preferences
|
||||
implementation("androidx.datastore:datastore-preferences:1.0.0")
|
||||
|
||||
|
||||
// Foundation for pager (Compose)
|
||||
implementation("androidx.compose.foundation:foundation:1.5.4")
|
||||
|
||||
|
||||
// Icons extended
|
||||
implementation("androidx.compose.material:material-icons-extended:1.5.4")
|
||||
|
||||
|
||||
// Lottie for animations
|
||||
implementation("com.airbnb.android:lottie-compose:6.1.0")
|
||||
|
||||
|
||||
// Coil for image loading
|
||||
implementation("io.coil-kt:coil-compose:2.5.0")
|
||||
|
||||
|
||||
// Crypto libraries for key generation
|
||||
implementation("org.bitcoinj:bitcoinj-core:0.16.2")
|
||||
implementation("org.bouncycastle:bcprov-jdk15to18:1.77")
|
||||
|
||||
|
||||
// Security for encrypted storage
|
||||
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
||||
|
||||
|
||||
// Room for database
|
||||
implementation("androidx.room:room-runtime:2.6.1")
|
||||
implementation("androidx.room:room-ktx:2.6.1")
|
||||
kapt("androidx.room:room-compiler:2.6.1")
|
||||
|
||||
annotationProcessor("androidx.room:room-compiler:2.6.1")
|
||||
|
||||
// Biometric authentication
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.rosetta.messenger.crypto
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* 🧪 Тесты кросс-платформенной совместимости шифрования
|
||||
*
|
||||
* Проверяют совместимость между:
|
||||
* - Kotlin (CryptoManager.kt)
|
||||
* - JS/React Native (cryptoJSI.ts)
|
||||
*
|
||||
* Алгоритм:
|
||||
* - PBKDF2-HMAC-SHA1 (1000 iterations, salt="rosetta")
|
||||
* - AES-256-CBC + PKCS7 padding
|
||||
* - zlib compression (deflate/inflate)
|
||||
* - Format: base64(iv):base64(ciphertext)
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CryptoCompatibilityTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
java.security.Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider())
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 🔐 Тестовые данные, зашифрованные на JS
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Эти данные были зашифрованы на JS (cryptoJSI.ts):
|
||||
*
|
||||
* const encrypted = encodeWithPasswordJSI("testPassword123", "Hello World");
|
||||
* console.log(encrypted);
|
||||
*
|
||||
* Параметры:
|
||||
* - password: "testPassword123"
|
||||
* - plaintext: "Hello World"
|
||||
* - salt: "rosetta"
|
||||
* - iterations: 1000
|
||||
* - hash: SHA1
|
||||
*/
|
||||
companion object {
|
||||
// ⚠️ TODO: Вставьте реальные зашифрованные данные из JS консоли
|
||||
// Для генерации выполните в RN/JS:
|
||||
//
|
||||
// import { encodeWithPasswordJSI } from './cryptoJSI';
|
||||
// console.log('Test 1:', encodeWithPasswordJSI("testPassword123", "Hello World"));
|
||||
// console.log('Test 2:', encodeWithPasswordJSI("mySecretPass", '{"key":"value","number":42}'));
|
||||
// console.log('Test 3:', encodeWithPasswordJSI("password", ""));
|
||||
// console.log('Test 4:', encodeWithPasswordJSI("пароль123", "Привет мир! 🔐"));
|
||||
|
||||
// Placeholder - замените реальными данными из JS
|
||||
const val JS_ENCRYPTED_HELLO_WORLD = "" // encodeWithPasswordJSI("testPassword123", "Hello World")
|
||||
const val JS_ENCRYPTED_JSON = "" // encodeWithPasswordJSI("mySecretPass", '{"key":"value","number":42}')
|
||||
const val JS_ENCRYPTED_EMPTY = "" // encodeWithPasswordJSI("password", "")
|
||||
const val JS_ENCRYPTED_CYRILLIC = "" // encodeWithPasswordJSI("пароль123", "Привет мир! 🔐")
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// ✅ Тесты Kotlin → Kotlin (базовая проверка)
|
||||
// ========================================
|
||||
|
||||
@Test
|
||||
fun kotlin_encrypt_decrypt_roundtrip() {
|
||||
val originalData = "Hello, World! This is a secret message."
|
||||
val password = "testPassword123"
|
||||
|
||||
val encrypted = CryptoManager.encryptWithPassword(originalData, password)
|
||||
val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
|
||||
|
||||
assertNotNull("Encrypted data should not be null", encrypted)
|
||||
assertTrue("Encrypted should contain ':'", encrypted.contains(":"))
|
||||
assertEquals("Decrypted should match original", originalData, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlin_encrypt_decrypt_empty_string() {
|
||||
val originalData = ""
|
||||
val password = "password"
|
||||
|
||||
val encrypted = CryptoManager.encryptWithPassword(originalData, password)
|
||||
val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
|
||||
|
||||
assertEquals("Empty string should roundtrip correctly", originalData, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlin_encrypt_decrypt_cyrillic_and_emoji() {
|
||||
val originalData = "Привет мир! 🔐 Тест кириллицы и эмодзи 🚀"
|
||||
val password = "пароль123"
|
||||
|
||||
val encrypted = CryptoManager.encryptWithPassword(originalData, password)
|
||||
val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
|
||||
|
||||
assertEquals("Cyrillic and emoji should roundtrip correctly", originalData, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlin_encrypt_decrypt_json() {
|
||||
val originalData = """{"key":"value","number":42,"nested":{"array":[1,2,3]}}"""
|
||||
val password = "jsonPassword"
|
||||
|
||||
val encrypted = CryptoManager.encryptWithPassword(originalData, password)
|
||||
val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
|
||||
|
||||
assertEquals("JSON should roundtrip correctly", originalData, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kotlin_wrong_password_returns_null() {
|
||||
val originalData = "Secret message"
|
||||
val correctPassword = "correctPassword"
|
||||
val wrongPassword = "wrongPassword"
|
||||
|
||||
val encrypted = CryptoManager.encryptWithPassword(originalData, correctPassword)
|
||||
val decrypted = CryptoManager.decryptWithPassword(encrypted, wrongPassword)
|
||||
|
||||
assertNull("Wrong password should return null", decrypted)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 🌐 Тесты JS → Kotlin (кросс-платформа)
|
||||
// ========================================
|
||||
|
||||
@Test
|
||||
fun js_to_kotlin_decrypt_hello_world() {
|
||||
if (JS_ENCRYPTED_HELLO_WORLD.isEmpty()) {
|
||||
println("⚠️ SKIP: JS_ENCRYPTED_HELLO_WORLD not set. Generate from JS first.")
|
||||
return
|
||||
}
|
||||
|
||||
val decrypted = CryptoManager.decryptWithPassword(JS_ENCRYPTED_HELLO_WORLD, "testPassword123")
|
||||
|
||||
assertEquals("Should decrypt JS data correctly", "Hello World", decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun js_to_kotlin_decrypt_json() {
|
||||
if (JS_ENCRYPTED_JSON.isEmpty()) {
|
||||
println("⚠️ SKIP: JS_ENCRYPTED_JSON not set. Generate from JS first.")
|
||||
return
|
||||
}
|
||||
|
||||
val decrypted = CryptoManager.decryptWithPassword(JS_ENCRYPTED_JSON, "mySecretPass")
|
||||
|
||||
assertEquals("Should decrypt JSON from JS", """{"key":"value","number":42}""", decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun js_to_kotlin_decrypt_empty() {
|
||||
if (JS_ENCRYPTED_EMPTY.isEmpty()) {
|
||||
println("⚠️ SKIP: JS_ENCRYPTED_EMPTY not set. Generate from JS first.")
|
||||
return
|
||||
}
|
||||
|
||||
val decrypted = CryptoManager.decryptWithPassword(JS_ENCRYPTED_EMPTY, "password")
|
||||
|
||||
assertEquals("Should decrypt empty string from JS", "", decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun js_to_kotlin_decrypt_cyrillic() {
|
||||
if (JS_ENCRYPTED_CYRILLIC.isEmpty()) {
|
||||
println("⚠️ SKIP: JS_ENCRYPTED_CYRILLIC not set. Generate from JS first.")
|
||||
return
|
||||
}
|
||||
|
||||
val decrypted = CryptoManager.decryptWithPassword(JS_ENCRYPTED_CYRILLIC, "пароль123")
|
||||
|
||||
assertEquals("Should decrypt Cyrillic from JS", "Привет мир! 🔐", decrypted)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 🔧 Тест формата данных
|
||||
// ========================================
|
||||
|
||||
@Test
|
||||
fun encrypted_format_is_correct() {
|
||||
val encrypted = CryptoManager.encryptWithPassword("test", "password")
|
||||
|
||||
val parts = encrypted.split(":")
|
||||
assertEquals("Should have exactly 2 parts (iv:ct)", 2, parts.size)
|
||||
|
||||
// Проверяем что обе части - валидный Base64
|
||||
try {
|
||||
val iv = android.util.Base64.decode(parts[0], android.util.Base64.NO_WRAP)
|
||||
val ct = android.util.Base64.decode(parts[1], android.util.Base64.NO_WRAP)
|
||||
|
||||
assertEquals("IV should be 16 bytes", 16, iv.size)
|
||||
assertTrue("Ciphertext should not be empty", ct.isNotEmpty())
|
||||
} catch (e: Exception) {
|
||||
fail("Both parts should be valid Base64: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 📊 Тест PBKDF2 ключа (для отладки)
|
||||
// ========================================
|
||||
|
||||
@Test
|
||||
fun pbkdf2_key_derivation_is_correct() {
|
||||
// Известный тестовый вектор для PBKDF2-HMAC-SHA1
|
||||
// password: "testPassword123", salt: "rosetta", iterations: 1000, keyLen: 32
|
||||
|
||||
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
|
||||
val spec = javax.crypto.spec.PBEKeySpec(
|
||||
"testPassword123".toCharArray(),
|
||||
"rosetta".toByteArray(Charsets.UTF_8),
|
||||
1000,
|
||||
256
|
||||
)
|
||||
val key = factory.generateSecret(spec).encoded
|
||||
|
||||
// Выводим ключ для сравнения с JS
|
||||
val keyHex = key.joinToString("") { "%02x".format(it) }
|
||||
println("🔑 PBKDF2 Key (hex): $keyHex")
|
||||
|
||||
assertEquals("Key should be 32 bytes", 32, key.size)
|
||||
|
||||
// ⚠️ TODO: Сравните этот ключ с JS:
|
||||
// const key = generatePBKDF2KeyJSI("testPassword123");
|
||||
// console.log("Key (hex):", key.toString('hex'));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -11,12 +14,14 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.RosettaAndroid"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.RosettaAndroid">
|
||||
android:theme="@style/Theme.RosettaAndroid"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
BIN
app/src/main/assets/emoji/0023-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/002a-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/0030-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/assets/emoji/0031-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/assets/emoji/0032-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/0033-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/0034-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/assets/emoji/0035-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/assets/emoji/0036-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/assets/emoji/0037-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/assets/emoji/0038-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/0039-fe0f-20e3.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/00a9-fe0f.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/assets/emoji/00ae-fe0f.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/assets/emoji/1f004.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/assets/emoji/1f0cf.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/assets/emoji/1f170-fe0f.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f171-fe0f.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/assets/emoji/1f17e-fe0f.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/1f17f-fe0f.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/assets/emoji/1f18e.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f191.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f192.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
app/src/main/assets/emoji/1f193.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f194.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f195.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f196.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/assets/emoji/1f197.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
app/src/main/assets/emoji/1f198.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/assets/emoji/1f199.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/1f19a.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1e8.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1e9.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1ea.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1eb.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1ec.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1ee.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f1.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f2.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f4.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f6.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f7.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f8.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1f9.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1fa.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1fc.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1fd.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/assets/emoji/1f1e6-1f1ff.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1e6.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1e7.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1e9.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ea.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1eb.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ec.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ed.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ee.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ef.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f1.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f2.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f3.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f4.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f6.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f7.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f8.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1f9.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1fb.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1fc.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1fe.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/assets/emoji/1f1e7-1f1ff.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1e6.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1e8.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1e9.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1eb.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1ec.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1ed.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1ee.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f0.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f1.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f2.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f3.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f4.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f5.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f6.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1f7.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1fa.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1fb.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1fc.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1fd.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1fe.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/assets/emoji/1f1e8-1f1ff.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1ea.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1ec.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1ef.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1f0.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1f2.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1f4.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/assets/emoji/1f1e9-1f1ff.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |