Track client and server sources
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d9f7603ae8
commit
de9dd05308
383 files changed
+44782
-2
No files matched your search
@@ -0,0 +1,142 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
fun String.asBuildConfigString(): String {
|
||||
return "\"${replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
||||
}
|
||||
|
||||
val serverUrl = providers.gradleProperty("SERVER_URL")
|
||||
.orElse(providers.environmentVariable("SERVER_URL"))
|
||||
.orElse("https://msgr.jeezft.xyz")
|
||||
.get()
|
||||
.trimEnd('/') + "/"
|
||||
|
||||
val webSocketUrl = providers.gradleProperty("WEB_SOCKET_URL")
|
||||
.orElse(providers.environmentVariable("WEB_SOCKET_URL"))
|
||||
.orElse("wss://msgr.jeezft.xyz")
|
||||
.get()
|
||||
.trimEnd('/')
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.gms)
|
||||
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.2.21"
|
||||
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("com.google.devtools.ksp")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.aiwazian.messenger"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.aiwazian.messenger"
|
||||
minSdk = 28
|
||||
targetSdk = 36
|
||||
versionCode = 15
|
||||
versionName = "1.7.0"
|
||||
|
||||
buildConfigField("String", "SERVER_URL", serverUrl.asBuildConfigString())
|
||||
buildConfigField("String", "WEB_SOCKET_URL", webSocketUrl.asBuildConfigString())
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
buildToolsVersion = "36.0.0"
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
includeInBundle = false
|
||||
}
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Firebase
|
||||
implementation(platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.messaging)
|
||||
implementation(libs.firebase.analytics)
|
||||
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.animation)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.material.icons.extended.android)
|
||||
implementation(libs.androidx.foundation)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
// DataStore
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
|
||||
implementation(libs.accompanist.systemuicontroller)
|
||||
implementation(libs.accompanist.navigation.material)
|
||||
implementation(libs.accompanist.navigation.animation)
|
||||
|
||||
implementation(libs.protobuf.javalite)
|
||||
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.converter.gson)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
// Ktor
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
implementation(libs.ktor.client.websockets)
|
||||
|
||||
implementation(libs.coil.compose)
|
||||
|
||||
// Lottie animation
|
||||
implementation(libs.lottie.compose)
|
||||
|
||||
implementation(libs.zxing.android.embedded)
|
||||
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.socket.io.client)
|
||||
|
||||
// Dagger Hilt
|
||||
implementation(libs.hilt.android)
|
||||
implementation(libs.androidx.graphics.shapes)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
ksp(libs.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
||||
// Room database
|
||||
implementation(libs.androidx.room.runtime)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
|
||||
implementation(libs.material.icons.extended)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
-keepattributes Signature
|
||||
|
||||
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
|
||||
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
|
||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
||||
|
||||
# With R8 full mode generic signatures are stripped for classes that are not
|
||||
# kept. Suspend functions are wrapped in continuations where the type argument
|
||||
# is used.
|
||||
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||
|
||||
# R8 full mode strips generic signatures from return types if not kept.
|
||||
-if interface * { @retrofit2.http.* public *** *(...); }
|
||||
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
|
||||
|
||||
# With R8 full mode generic signatures are stripped for classes that are not kept.
|
||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
||||
|
||||
# Deleting all calls Log.v, Log.d, Log.i, Log.w, Log.e, Log.wtf
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static *** d(...);
|
||||
public static *** e(...);
|
||||
public static *** i(...);
|
||||
public static *** v(...);
|
||||
public static *** w(...);
|
||||
public static *** wtf(...);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.CAMERA"
|
||||
tools:node="remove" />
|
||||
|
||||
<application
|
||||
android:name=".Application"
|
||||
android:allowBackup="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/new_app_icon"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/new_app_icon"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Messenger"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="DataExtractionRules"
|
||||
tools:targetApi="33">
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.aiwazian.messenger.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<service
|
||||
android:name=".services.NotificationService"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Messenger" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Messenger">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":120,"w":512,"h":512,"nm":"13_FOLDER_EMOJI_closed","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[244.941,205.599,0]},"a":{"a":0,"k":[60,60,0]}},"ao":0,"ip":0,"op":85,"st":5,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 64","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":5,"s":[56.544,13.819,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[76.544,73.819,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":45,"s":[76.544,73.819,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":73,"s":[59.552,234.809,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":5,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"t":73,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921568627,0.462745098039,0.513725490196,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[20]},{"t":25,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.462745127958,0.513725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":5,"op":69,"st":5,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Layer 63","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":8,"s":[49.949,0.843,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[59.96,60.851,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":56,"s":[59.96,60.851,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":84,"s":[60.923,235.808,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":28,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":38,"s":[100,100,100]},{"t":76,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921568627,0.462745098039,0.513725490196,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[20]},{"t":28,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.462745127958,0.513725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":80,"st":8,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Layer 62","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":11,"s":[33.63,-13.14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[53.676,46.687,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"t":41,"s":[43.676,47.169,0],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":61,"s":[43.676,47.169,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":89,"s":[61.423,235.808,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":11,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":41,"s":[100,100,100]},{"t":79,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":41,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}]},{"i":{"x":0.715,"y":0.702},"o":{"x":1,"y"Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"11_docs_3_perebor_v1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[256.072,425.476,0]},"a":{"a":0,"k":[60,60,0]}},"ao":0,"ip":-52,"op":216,"st":-52,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 11","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[59.374,144.509,0]},"a":{"a":0,"k":[255.446,479.986,0]},"s":{"a":1,"k":[{"i":{"x":[0.33,0.33,0.33],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.33,0.33,0.33],"y":[1,1,1]},"o":{"x":[0.67,0.67,0.67],"y":[0,0,0]},"t":6,"s":[106,92,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.873,0.888,0.67],"y":[0,0,0]},"t":139,"s":[106,92,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":156,"s":[100,104,100]},{"t":170,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.33,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0.274,0.959],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[3.912,-2.788]],"o":[[-0.482,-0.836],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-2.381,4.367],[0,0]],"v":[[56.339,13.151],[55.194,10.452],[49.747,-8.581],[43.677,-13.151],[-34.539,-13.151],[-40.085,-9.857],[-46.732,2.335],[-56.339,13.151]],"c":true}]},{"i":{"x":0.33,"y":1},"o":{"x":0.67,"y":0},"t":6,"s":[{"i":[[0,0],[0.274,0.959],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[3.912,-2.788]],"o":[[-0.482,-0.836],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-2.381,4.367],[0,0]],"v":[[56.339,13.151],[55.194,10.452],[49.747,-8.581],[43.677,-13.151],[-34.539,-13.151],[-40.085,-9.857],[-46.732,2.335],[-56.339,13.151]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.67,"y":0},"t":139,"s":[{"i":[[0,0],[0.274,0.959],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[3.912,-2.788]],"o":[[-0.482,-0.836],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-2.381,4.367],[0,0]],"v":[[56.339,13.151],[55.194,10.452],[49.747,-8.581],[43.677,-13.151],[-34.539,-13.151],[-40.085,-9.857],[-46.732,2.335],[-56.339,13.151]],"c":true}]},{"t":166,"s":[{"i":[[0,0],[0.274,0.959],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[3.912,-2.788]],"o":[[-0.482,-0.836],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-2.381,4.367],[0,0]],"v":[[56.339,13.151],[55.194,10.452],[49.747,-8.581],[43.677,-13.151],[-34.539,-13.151],[-40.085,-9.857],[-46.732,2.335],[-56.339,13.151]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.101960785687,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[401.878,166.98]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.33,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0.385,0.956],[2.888,0],[0,0],[1.474,5.149],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[11.806,0],[0,0],[0.944,-0.412],[-0.081,-2.887],[0,0],[-3.925,0],[0,0],[-0.11,3.923]],"o":[[0.029,-1.03],[-1.157,-2.868],[0,0],[-5.356,0],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-5.652,10.366],[0,0],[-1.03,0],[-2.834,1.237],[0,0],[0.11,3.923],[0,0],[3.925,0],[0,0]],"v":[[226.94,-117.16],[226.449,-120.186],[219.697,-124.776],[213.162,-124.776],[201.625,-133.475],[196.178,-152.508],[190.108,-157.078],[111.892,-157.078],[106.346,-153.784],[99.699,-141.592],[71.378,-124.776],[-219.53,-124.776],[-222.541,-124.201],[-226.941,-117.323],[-219.444,150.031],[-212.197,157.078],[212.2,157.078],[219.448,150.031]],"c":true}]},{"i":{"x":0.33,"y":1},"o":{"x":0.67,"y":0},"t":6,"s":[{"i":[[0,0],[0.385,0.956],[2.888,0],[0,0],[1.474,5.149],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[11.806,0],[0,0],[0.944,-0.412],[-0.081,-2.887],[0,0],[-3.925,0],[0,0],[-0.11,3.923]],"o":[[0.029,-1.03],[-1.157,-2.868],[0,0],[-5.356,0],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-5.652,10.366],[0,0],[-1.03,0],[-2.834,1.237],[0,0],[0.11,3.923],[0,0],[3.925,0],[0,0]],"v":[[226.94,-117.16],[226.449,-120.186],[219.697,-124.776],[213.162,-124.776],[201.625,-133.475],[196.178,-152.508],[190.108,-157.078],[111.892,-157.078],[106.346,-153.784],[99.699,-141.592],[71.378,-124.776],[-219.53,-124.776],[-222.541,-124.201],[-226.941,-117.323],[-207.194,150.031],[-199.947,157.078],[199.969,157.078],[207.216,150.031]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.67,"y":0},"t":139,"s":[{"i":[[0,0],[0.385,0.956],[2.888,0],[0,0],[1.474,5.149],[0,0],[2.8,0],[0,0],[1.071,-2.012],[0,0],[11.806,0],[0,0],[0.944,-0.412],[-0.081,-2.887],[0,0],[-3.925,0],[0,0],[-0.11,3.923]],"o":[[0.029,-1.03],[-1.157,-2.868],[0,0],[-5.356,0],[0,0],[-0.773,-2.691],[0,0],[-2.339,0],[0,0],[-5.652,10.366],[0,0],[-1.03,0],[-2.834,1.237],[0,0],[0.11,3.923],[0,0],[3.925,0],[0,0]],"v":[[226.94,-117.16],[226.449,-120.186],[219.697,-124.776],[213.162,-124.776],[201.625,-133.475],[196.178,-152.508],[190.108,-157.078],[111.Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":157,"w":512,"h":512,"nm":"KEY 1 MAIN NEW","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"SCALE ALL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[251,266,0]},"s":{"a":0,"k":[94,94,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"NULL MOVE","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[0.891]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.38],"y":[-0.057]},"t":9,"s":[-10]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":56,"s":[53]},{"i":{"x":[0.39],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":73,"s":[15]},{"i":{"x":[0.806],"y":[1.639]},"o":{"x":[0.7],"y":[0]},"t":104,"s":[50]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.279],"y":[0.085]},"t":117,"s":[53]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":139,"s":[-2]},{"t":156,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0,0,0],"to":[5.625,-3.625,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.757},"o":{"x":0.256,"y":0},"t":12,"s":[7.75,-11.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.4,"y":0.041},"t":36,"s":[-24.5,3.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":55,"s":[-106.5,-14.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":0.51},"o":{"x":0.8,"y":0},"t":73,"s":[-107,-4.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.6,"y":0.929},"o":{"x":0.7,"y":0.045},"t":102,"s":[-106.5,-14.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.38,"y":1},"o":{"x":0.2,"y":0.198},"t":123,"s":[-17.5,3.5,0],"to":[4.625,0.375,0],"ti":[0,0,0]},{"t":146,"s":[0,0,0]}]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.38,0.38,0.38],"y":[0,0,0]},"t":12,"s":[97,97,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":36,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":55,"s":[100,100,100]},{"i":{"x":[0.39,0.39,0.39],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.8],"y":[0,0,0]},"t":73,"s":[100,100,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":102,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":121,"s":[100,100,100]},{"t":156,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":184,"st":4,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Head","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"t":50,"s":[100],"h":1},{"t":70,"s":[0],"h":1},{"t":81,"s":[100],"h":1}]},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":56,"s":[-45]},{"i":{"x":[0.39],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":73,"s":[-94]},{"t":104,"s":[-45]}]},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.38,"y":0},"t":12,"s":[21.902,-19.229,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.4,"y":0},"t":37,"s":[13.7,-10.457,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.59,"y":0.531},"o":{"x":0.512,"y":0},"t":56,"s":[14.344,-15.846,0],"to":[5.763,1.954,0],"ti":[-1.432,-3.618,0]},{"i":{"x":0.521,"y":1},"o":{"x":0.219,"y":0.372},"t":65,"s":[43.531,-3.468,0],"to":[1.051,6.624,0],"ti":[0,0,0]},{"i":{"x":0.281,"y":1},"o":{"x":0.8,"y":0},"t":73,"s":[41.554,15.476,0],"to":[23.745,-25.52,0],"ti":[0,0,0]},{"i":{"x":0.6,"y":1},"o":{"x":0.7,"y":0},"t":104,"s":[21.902,-19.229,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.2,"y":0},"t":121,"s":[14.446,-11.123,0],"to":[0,0,0],"ti":[0,0,0]},{"t":144,"s":[21.902,-19.229,0]}]},"a":{"a":0,"k":[-11.848,-84.075,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.38,0.38,0.38],"y":[0,0,0]},"t":12,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":39,"s":[119,119,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":56,"s":[98,98,100]},{"i":{"x":[0.39,0.39,0.39],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.8],"y":[0,0,0]},"t":73,"s":[98,96,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":104,"s":[98,98,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":121,"s":[120,120,100]},{"t":144,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[-3.677,-1.485],[-3.741,3.741],[-7.667,-1.211],[-27.386,27.386],[-1.52,31.845]],"o":[[2.058,-1.662],[8.882,0.014],[4.893,1.98],[0,0],[49.011,7.743],[24.319,-24.319],[0,0]],"v":[[-13.246,39.594],[12.219,38.373],[36.883,48.527],[51.117,45.635],[67.176,36.124],[186.005,-2.958],[224.763,-90.524]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.38,"y":0},"t":12,"s":[{"i":[[0,0],[0,0],[-3.677,-1.485],[-3.741,3.741],[-7.667,-1.211],[-27.386,27.386],[-1.52,31.845]],"o":[[2.058,-1.662],[8.882,0.014],[4.893,1.98],[0,0],[49.011,7.743],[24.319,-24.319],[0,0]],"v":[[-13.246,39.594],[12.219,38.373],[36.883,48.527],[51.117,45.Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":157,"w":512,"h":512,"nm":"KEY IRON MAIN","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"SCALE ALL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[255,256,0]},"s":{"a":0,"k":[97,97,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"NULL ALL","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[-37]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.313],"y":[0]},"t":9,"s":[-47]},{"i":{"x":[0.39],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":57,"s":[4]},{"i":{"x":[0.806],"y":[1.59]},"o":{"x":[0.7],"y":[0]},"t":104,"s":[4]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.279],"y":[0.106]},"t":116,"s":[7]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":139,"s":[-39]},{"t":156,"s":[-37]}]},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0,0,0],"to":[5.625,-3.625,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.568},"o":{"x":0.256,"y":0},"t":12,"s":[7.75,-11.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.4,"y":0.034},"t":36,"s":[-5.5,-16.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":0.204},"o":{"x":0.4,"y":0},"t":55,"s":[-106.5,-31.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.6,"y":0.948},"o":{"x":0.7,"y":0.033},"t":102,"s":[-106.5,-31,0],"to":[61.5,2.5,0],"ti":[0,0,0]},{"i":{"x":0.38,"y":1},"o":{"x":0.2,"y":0.298},"t":123,"s":[-7.5,-8.5,0],"to":[4.625,0.375,0],"ti":[0,0,0]},{"t":146,"s":[0,0,0]}]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.38,0.38,0.38],"y":[0,0,0]},"t":12,"s":[97,97,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":36,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":55,"s":[103,103,100]},{"i":{"x":[0.39,0.39,0.39],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.8],"y":[0,0,0]},"t":73,"s":[103,103,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":102,"s":[103,103,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":120,"s":[100,100,100]},{"t":156,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Middle circles","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"t":50,"s":[100],"h":1},{"t":66,"s":[0],"h":1},{"t":89,"s":[100],"h":1}]},"p":{"a":0,"k":[8.089,-11.266,0]},"a":{"a":0,"k":[8.089,-11.266,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.38,"y":0},"t":12,"s":[{"i":[[0,0],[-0.038,-3.079],[0,0]],"o":[[3.079,-0.038],[0,0],[0,0]],"v":[[32.5,-50.309],[38.143,-44.803],[38.955,21.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":37,"s":[{"i":[[0,0],[4.937,-4.825],[-14.312,-14.791]],"o":[[-2.163,1.564],[-6.592,6.442],[3.822,3.95]],"v":[[53.138,-40.27],[46.107,-34.605],[49.741,14.268]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":44,"s":[{"i":[[0,0],[5.087,-4.667],[-13.839,-15.235]],"o":[[-2.211,1.495],[-6.792,6.231],[3.696,4.068]],"v":[[44.735,-41.702],[37.529,-36.263],[39.621,12.701]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.8,"y":0},"t":56,"s":[{"i":[[0,0],[5.087,-4.667],[-13.48,-13.938]],"o":[[-2.211,1.495],[-6.792,6.231],[3.821,3.951]],"v":[[48.659,-37.786],[41.99,-32.384],[46.222,12.374]],"c":false}]},{"i":{"x":0.231,"y":1},"o":{"x":0.167,"y":0.05},"t":63,"s":[{"i":[[0,0],[0.947,-1.656],[-13.48,-13.938]],"o":[[0,0],[-3.246,5.677],[3.821,3.951]],"v":[[39.212,-27.894],[39.153,-27.622],[46.222,12.374]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.8,"y":0},"t":73,"s":[{"i":[[0,0],[0.546,-1.09],[-12.115,-14.974]],"o":[[0.361,0.345],[-3.497,4.467],[3.448,4.261]],"v":[[38.848,-27.089],[39.237,-27.047],[45.336,12.171]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87,"s":[{"i":[[0,0],[1.514,-1.852],[-12.406,-14.753]],"o":[[-0.187,0.59],[-4.199,4.843],[3.527,4.195]],"v":[[41.496,-29.137],[40.381,-27.953],[45.525,12.214]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[3.625,-3.515],[-13.04,-14.272]],"o":[[-1.383,1.124],[-5.731,5.663],[3.701,4.051]],"v":[[37.584,-26.223],[35.45,-25.003],[45.936,12.309]],"c":false}]},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":104,"s":[{"i":[[0,0],[5.087,-4.667],[-13.48,-13.938]],"o":[[-2.211,1.495],[-6.792,6.231],[3.821,3.951]],"v":[[48.659,-37.786],[41.99,-32.384],[46.222,12.374]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":120,"s":[{"i":[[0,0],[4.937,-4.825],[-14.312,-14.791]],"o":[[-2.163,1.564],[-6.592,6.442],[3.822,3.95]],"v":[[45.606,-42.925],[38.575,-37.261],[41.187,12.199]],"c":false}]},{"t":144,"s":[{"i":[[0,0],[-0.038,-3.079],[0,0]],"o":[[3.079,-0.038],[0,0],[0,0]],"v":[[32.5,-50.309],[38.143,-44.803],[38.955,21.504]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.207843139768,0.188235297799,0.184313729405,1]}Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"key","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":3,"nm":"Null 1117","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[242.5,283.5,0]},"a":{"a":0,"k":[126.923,113.462,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[104,104,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":40,"s":[103,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":95,"s":[103,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":137,"s":[103,103,100]},{"t":179,"s":[104,104,100]}]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","parent":2,"sr":1,"ks":{"r":{"a":0,"k":-49.844},"p":{"a":0,"k":[-36.5,21.5,0]},"a":{"a":0,"k":[139,-42,0]},"s":{"a":0,"k":[90,90,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139,-42],[139,-123]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[6]},{"t":82,"s":[0]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[124,-32]},"a":{"a":0,"k":[139,-42]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":-35},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139,-42],[139,-123]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[6]},{"t":82,"s":[0]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[109,-22]},"a":{"a":0,"k":[139,-42]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":-70},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139,-42],[139,-123]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[6]},{"t":82,"s":[0]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[169,-22]},"a":{"a":0,"k":[139,-42]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":70},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139,-42],[139,-123]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[6]},{"t":82,"s":[0]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[154,-32]},"a":{"a":0,"k":[139,-42]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":35},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139,-42],[139,-123]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[6]},{"t":82,"s":[0]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":74,"s":[0]},{"t":82,"s":[100]}]},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"t":77.5,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":66,"op":82,"st":-24,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"click","parent":2,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64,"s":[136.5,78.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[140.5,80.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":69,"s":[136.5,78.5,0],"to":[0,0,0],"ti":[0,0,0]},{Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":139,"w":512,"h":512,"nm":"Comp 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Режим изоляции 25","sr":1,"ks":{"p":{"a":0,"k":[279.244,361.486,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[93,93,100]},{"t":123,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.548,0],[0,0],[0,3.548],[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0]],"o":[[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0],[3.548,0],[0,0],[0,3.548]],"v":[[7.785,14.209],[-7.785,14.209],[-14.209,7.785],[-14.209,-7.785],[-7.785,-14.209],[7.785,-14.209],[14.209,-7.785],[14.209,7.785]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Режим изоляции 24","sr":1,"ks":{"p":{"a":0,"k":[232.977,361.486,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[93,93,100]},{"t":123,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.548,0],[0,0],[0,3.548],[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0]],"o":[[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0],[3.548,0],[0,0],[0,3.548]],"v":[[7.785,14.209],[-7.785,14.209],[-14.209,7.785],[-14.209,-7.785],[-7.785,-14.209],[7.785,-14.209],[14.209,-7.785],[14.209,7.785]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.282352954149,0.301960796118,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Режим изоляции 23","sr":1,"ks":{"p":{"a":0,"k":[186.711,361.486,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[93,93,100]},{"t":123,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.548,0],[0,0],[0,3.548],[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0]],"o":[[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0],[3.548,0],[0,0],[0,3.548]],"v":[[7.785,14.209],[-7.785,14.209],[-14.209,7.785],[-14.209,-7.785],[-7.785,-14.209],[7.785,-14.209],[14.209,-7.785],[14.209,7.785]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.760784327984,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Режим изоляции 22","sr":1,"ks":{"p":{"a":0,"k":[325.51,310.975,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[93,93,100]},{"t":123,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.548,0],[0,0],[0,3.548],[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0]],"o":[[0,0],[-3.548,0],[0,0],[0,-3.548],[0,0],[3.548,0],[0,0],[0,3.548]],"v":[[7.785,14.209],[-7.785,14.209],[-14.209,7.785],[-14.209,-7.785],[-7.785,-14.209],[7.785,-14.209],[14.209,-7.785],[14.209,7.785]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.65098041296,0.631372570992,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Режим изоляции 21","sr":1,"ks":{"p":{"a":0,"k":[279.244,310.975,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[93,93,100]},{"t":123,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"tgs":1,"v":"5.5.2.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"_016_SRCH_OUT","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":3,"ty":4,"nm":"lupa11","parent":4,"sr":1,"ks":{"o":{"a":0,"k":50},"r":{"a":0,"k":180},"s":{"a":0,"k":[100,102.141,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":100,"s":[{"i":[[-0.881,2.494],[-7.829,1.937]],"o":[[2.657,-7.518],[1.846,-0.457]],"v":[[-22.559,-7.972],[-5.765,-23.22]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":114,"s":[{"i":[[-0.002,2.181],[-2.688,2.11]],"o":[[0.006,-6.576],[0.634,-0.498]],"v":[[-26.253,-6.948],[-21.953,-20.95]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":140,"s":[{"i":[[-0.002,2.181],[-2.688,2.11]],"o":[[0.006,-6.576],[0.634,-0.498]],"v":[[-26.253,-6.948],[-21.953,-20.95]],"c":false}]},{"t":158,"s":[{"i":[[-0.881,2.494],[-7.829,1.937]],"o":[[2.657,-7.518],[1.846,-0.457]],"v":[[-22.559,-7.972],[-5.765,-23.22]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":84,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":96,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":108,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":120,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":132,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":144,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":156,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":168,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":180,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":192,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":204,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":216,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":228,"s":[0]},{"t":240,"s":[5]}]},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":84,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":96,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":108,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":120,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":132,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":144,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":156,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":168,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":180,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":192,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":204,"s":[95]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":216,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":228,"s":[95]},{"t":240,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":60,"op":240,"st":60,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"lupa10","parent":6,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":68,"s":[0]},{"t":80,"s":[21],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":100,"s":[21]},{"t":114,"s":[0],"h":1}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":100,"s":[{"i":[[-0.53,1.978],[-7.467,2.569]],"o":[[2.059,-7.69],[2.441,-0.84]],"v":[[-23.107,-6.202],[-7.786,-22.624]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":114,"s":[{"i":[[0.379,1.578],[-2,3.001]],"o":[[-1.474,-6.136],[0.654,-0.981]],"v":[[-23.147,12.142],[-22.278,-2.492]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":140,"s":[{"i":[[0.379,1.578],[-2,3.001]],"o":[[-1.474,-6.136],[0.654,-0.981]],"v":[[-23.147,12.142],[-22.278,-2.492]],"c":false}]},{"t":158,"s":[{"i":[[-0.53,1.978],[-7.467,2.569]],"o":[[2.059,-7.69],[2.441,-0.84]],"v":[[-23.107,-6.202],[-7.786,-22.624]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},Line truncated
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.aiwazian.messenger
|
||||
|
||||
import android.app.Application
|
||||
import com.google.firebase.FirebaseApp
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import java.util.Locale
|
||||
|
||||
@HiltAndroidApp
|
||||
class Application : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Locale.setDefault(Locale.forLanguageTag("ru"))
|
||||
FirebaseApp.initializeApp(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aiwazian.messenger
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.aiwazian.messenger.ui.login.AuthScreen
|
||||
import com.aiwazian.messenger.ui.theme.ApplicationTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LoginActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
setContent {
|
||||
ApplicationTheme(dynamicColor = true) {
|
||||
AuthScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.aiwazian.messenger
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.aiwazian.messenger.database.repository.UserRepository
|
||||
import com.aiwazian.messenger.services.AppLockService
|
||||
import com.aiwazian.messenger.services.DataStoreManager
|
||||
import com.aiwazian.messenger.services.NotificationService
|
||||
import com.aiwazian.messenger.services.ThemeService
|
||||
import com.aiwazian.messenger.services.TokenManager
|
||||
import com.aiwazian.messenger.services.UserManager
|
||||
import com.aiwazian.messenger.ui.ChatScreen
|
||||
import com.aiwazian.messenger.ui.LockScreen
|
||||
import com.aiwazian.messenger.ui.MainScreen
|
||||
import com.aiwazian.messenger.ui.element.NavigationController
|
||||
import com.aiwazian.messenger.ui.theme.ApplicationTheme
|
||||
import com.aiwazian.messenger.utils.ChatState
|
||||
import com.aiwazian.messenger.utils.WebSocketManager
|
||||
import com.aiwazian.messenger.viewModels.NavigationViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var appLockService: AppLockService
|
||||
|
||||
@Inject
|
||||
lateinit var themeService: ThemeService
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
lateinit var navViewModel: NavigationViewModel
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
DataStoreManager.initialize(newBase)
|
||||
runBlocking { TokenManager.init() }
|
||||
super.attachBaseContext(newBase)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (TokenManager.getToken().isBlank()) {
|
||||
startActivity(
|
||||
Intent(
|
||||
this,
|
||||
LoginActivity::class.java
|
||||
).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
TokenManager.setUnauthorizedCallback {
|
||||
val intent = Intent(
|
||||
this,
|
||||
LoginActivity::class.java
|
||||
).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
this@MainActivity.startActivity(intent)
|
||||
this@MainActivity.finish()
|
||||
}
|
||||
|
||||
setContent {
|
||||
val isLockApp by appLockService.isLockApp.collectAsState()
|
||||
val selectedTheme by themeService.currentTheme.collectAsState()
|
||||
val selectedColor by themeService.primaryColor.collectAsState()
|
||||
val isDynamicColorEnable by themeService.dynamicColor.collectAsState()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
WebSocketManager.onConnect = {
|
||||
lifecycleScope.launch {
|
||||
UserManager.loadUserData(userRepository)
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketManager.onClose = { code ->
|
||||
if (code == 1008) {
|
||||
TokenManager.getUnauthorizedCallback()?.invoke()
|
||||
} else {
|
||||
lifecycleScope.launch {
|
||||
delay(1000)
|
||||
WebSocketManager.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketManager.onFailure = {
|
||||
lifecycleScope.launch {
|
||||
delay(1000)
|
||||
WebSocketManager.connect()
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketManager.connect()
|
||||
|
||||
UserManager.loadUserData(userRepository)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"MainActivity",
|
||||
"Ошибка подключения вебсокета",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
val notificationService = NotificationService()
|
||||
val token = notificationService.getFirebaseToken()
|
||||
notificationService.sendTokenToServer(token)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"MainActivity",
|
||||
"Ошибка при отправке токена для уведомлений на сервер",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationTheme(
|
||||
theme = selectedTheme,
|
||||
dynamicColor = isDynamicColorEnable,
|
||||
primaryColor = selectedColor.color
|
||||
) {
|
||||
navViewModel = viewModel<NavigationViewModel>()
|
||||
|
||||
NavigationController {
|
||||
MainScreen()
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isLockApp,
|
||||
enter = fadeIn(tween(100)),
|
||||
exit = fadeOut(tween(100))
|
||||
) {
|
||||
LockScreen()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val chatId = intent.getStringExtra("chatId")?.toLongOrNull()
|
||||
|
||||
if (chatId != null && !ChatState.isChatOpen(chatId)) {
|
||||
navViewModel.addScreenInStack {
|
||||
ChatScreen(chatId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
|
||||
val chatId = intent.getStringExtra("chatId")?.toLongOrNull()
|
||||
|
||||
if (chatId != null) {
|
||||
if (!ChatState.isChatOpen(chatId)) {
|
||||
navViewModel.addScreenInStack {
|
||||
ChatScreen(chatId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.aiwazian.messenger.api
|
||||
|
||||
import com.aiwazian.messenger.services.TokenManager
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
class AuthInterceptor(
|
||||
private val getToken: () -> String?,
|
||||
private val shouldSkipAuth: (String) -> Boolean,
|
||||
private val onUnauthorized: (() -> Unit)?
|
||||
) : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val path = request.url.encodedPath
|
||||
|
||||
if (shouldSkipAuth(path)) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
val token = getToken()
|
||||
val authRequest = if (!token.isNullOrEmpty()) {
|
||||
request.newBuilder()
|
||||
.addHeader(
|
||||
"Authorization",
|
||||
"Bearer $token"
|
||||
)
|
||||
.build()
|
||||
} else {
|
||||
request
|
||||
}
|
||||
|
||||
val response = chain.proceed(authRequest)
|
||||
|
||||
if (response.code == 401 && TokenManager.isAuthorized()) {
|
||||
TokenManager.setAuthorized(false)
|
||||
onUnauthorized?.invoke()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aiwazian.messenger.api
|
||||
|
||||
import com.aiwazian.messenger.utils.ProgressResponseBody
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
class ProgressInterceptor(
|
||||
private val onProgress: (url: String, progress: Int) -> Unit
|
||||
) : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val response = chain.proceed(request)
|
||||
val url = request.url.toString()
|
||||
val body = response.body
|
||||
return response.newBuilder()
|
||||
.body(
|
||||
ProgressResponseBody(
|
||||
url,
|
||||
body,
|
||||
onProgress
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.aiwazian.messenger.api
|
||||
|
||||
import com.aiwazian.messenger.interfaces.ApiService
|
||||
import com.aiwazian.messenger.services.TokenManager
|
||||
import com.aiwazian.messenger.utils.Constants
|
||||
import com.aiwazian.messenger.utils.DownloadManager
|
||||
import com.aiwazian.messenger.utils.Route
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.lang.reflect.Type
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private object LongTypeAdapter : JsonDeserializer<Long>, JsonSerializer<Long> {
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Long {
|
||||
return when {
|
||||
json.isJsonPrimitive && json.asJsonPrimitive.isNumber -> json.asLong
|
||||
json.isJsonPrimitive -> json.asString.toLongOrNull() ?: 0L
|
||||
else -> 0L
|
||||
}
|
||||
}
|
||||
override fun serialize(src: Long, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src)
|
||||
}
|
||||
}
|
||||
|
||||
object RetrofitInstance {
|
||||
|
||||
private val gson = GsonBuilder()
|
||||
.registerTypeAdapter(Long::class.java, LongTypeAdapter)
|
||||
.registerTypeAdapter(Long::class.javaObjectType, LongTypeAdapter)
|
||||
.create()
|
||||
|
||||
private val BASE_URL = Constants.SERVER_URL
|
||||
|
||||
private val skipAuthPaths = listOf(
|
||||
Route.LOGIN,
|
||||
Route.REGISTER,
|
||||
Route.FIND_USER_BY_LOGIN
|
||||
)
|
||||
|
||||
private val okHttpClient = OkHttpClient.Builder().connectTimeout(
|
||||
1,
|
||||
TimeUnit.MINUTES
|
||||
).readTimeout(
|
||||
1,
|
||||
TimeUnit.MINUTES
|
||||
).writeTimeout(
|
||||
1,
|
||||
TimeUnit.MINUTES
|
||||
).addInterceptor(
|
||||
AuthInterceptor(
|
||||
getToken = {
|
||||
TokenManager.getToken()
|
||||
},
|
||||
shouldSkipAuth = { path ->
|
||||
skipAuthPaths.contains(path)
|
||||
},
|
||||
onUnauthorized = {
|
||||
TokenManager.getUnauthorizedCallback()?.invoke()
|
||||
})
|
||||
).addNetworkInterceptor(
|
||||
ProgressInterceptor { url, progress ->
|
||||
DownloadManager.updateProgress(
|
||||
url,
|
||||
progress
|
||||
)
|
||||
}).build()
|
||||
|
||||
val api: ApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class ApiResponse(
|
||||
@Keep val ok: Boolean,
|
||||
@Keep val message: String
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class UsernameAvailability(
|
||||
@Keep val available: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class Attachment(
|
||||
@Keep val id: String,
|
||||
@Keep val messageId: Int = 0,
|
||||
@Keep val chatId: Long = 0,
|
||||
@Keep val name: String,
|
||||
@Keep val url: String = "",
|
||||
@Keep val size: Long,
|
||||
@Keep val mimeType: String? = null
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class FileDownloadUrlResponse(
|
||||
val downloadUrl: String,
|
||||
val name: String,
|
||||
val size: String,
|
||||
val mimeType: String
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class FileUploadInitRequest(val name: String, val size: Long, val mimeType: String)
|
||||
|
||||
@Keep
|
||||
data class FileUploadInitResponse(val signedUrl: String, val fileId: String)
|
||||
|
||||
@Keep
|
||||
data class FileUploadConfirmRequest(val fileId: String, val text: String? = null)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class AuthRequest(
|
||||
@Keep val login: String,
|
||||
@Keep val password: String,
|
||||
@Keep val deviceModel: String,
|
||||
@Keep val osVersion: String,
|
||||
@Keep val osName: String
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class AuthResponse(
|
||||
@Keep val token: String,
|
||||
@Keep val userId: String
|
||||
)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
data class ChangeCloudPasswordRequest(
|
||||
val password: String
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.aiwazian.messenger.enums.ChannelType
|
||||
import com.aiwazian.messenger.interfaces.Profile
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class ChannelInfo(
|
||||
@Keep override val id: Long = 0,
|
||||
@Keep val ownerId: Long = 0,
|
||||
@Keep val name: String = "",
|
||||
@Keep val bio: String = "",
|
||||
@Keep val subscribers: Int = 0,
|
||||
@Keep val removedUser: Int = 0,
|
||||
@Keep var channelType: Int = ChannelType.PRIVATE.ordinal,
|
||||
@Keep var publicLink: String? = null,
|
||||
@Keep val isSubscribed: Boolean = false
|
||||
) : Profile
|
||||
|
||||
@Keep
|
||||
data class CreateChannelRequest(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("bio") val bio: String? = null,
|
||||
@SerializedName("channelType") val channelType: String,
|
||||
@SerializedName("username") val username: String? = null
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class CreatedEntityResponse(@SerializedName("id") val id: Long)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.aiwazian.messenger.enums.ChatType
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class ChatInfo(
|
||||
@Keep var id: Long = 0,
|
||||
@Keep @SerializedName("name") var chatName: String = "",
|
||||
@Keep var isPinned: Boolean = false,
|
||||
@Keep var lastMessage: Message? = null
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
data class CustomColors(
|
||||
val secondary: Color,
|
||||
val background: Color,
|
||||
var primary: Color,
|
||||
val text: Color,
|
||||
val textHint: Color,
|
||||
val topAppBarBackground: Color,
|
||||
val sendMessageTimeBackground: Color,
|
||||
val danger: Color,
|
||||
val dangerBackground: Color,
|
||||
val horizontalDivider: Color
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class DeleteChatPayload (
|
||||
val chatId: Long
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class DeleteMessagePayload(
|
||||
val chatId: Long,
|
||||
val messageId: Int
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import com.aiwazian.messenger.enums.DownloadStatus
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
||||
|
||||
data class DownloadItem(
|
||||
val fileId: String = "",
|
||||
val url: String,
|
||||
val fileName: String,
|
||||
var progress: Int = 0,
|
||||
var status: DownloadStatus = DownloadStatus.PENDING,
|
||||
var call: Call<ResponseBody>? = null,
|
||||
var onComplete: (() -> Unit)? = null
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
data class DropdownMenuAction(
|
||||
val icon: ImageVector,
|
||||
val text: String,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class FolderInfo(
|
||||
@Keep var id: Int = 0,
|
||||
@Keep var name: String = "",
|
||||
@Keep var chats: List<ChatInfo> = emptyList()
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.aiwazian.messenger.interfaces.Profile
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class GroupInfo(
|
||||
@Keep override val id: Long = 0,
|
||||
@Keep val ownerId: Long = 0,
|
||||
@Keep val name: String = "",
|
||||
@Keep val bio: String = "",
|
||||
@Keep val members: Int = 0
|
||||
) : Profile
|
||||
|
||||
@Keep
|
||||
data class CreateGroupRequest(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("bio") val bio: String? = null
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class HistoryClearPayload(
|
||||
val chatId: String
|
||||
)
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class KanbanBoard(
|
||||
val id: Int = 0,
|
||||
val title: String = "",
|
||||
val columns: List<KanbanColumn> = emptyList(),
|
||||
val updatedAt: Long = 0
|
||||
)
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class KanbanColumn(
|
||||
val id: Int = 0,
|
||||
val boardId: Int = 0,
|
||||
val title: String = "",
|
||||
val position: Int = 0,
|
||||
val tasks: List<KanbanTask> = emptyList()
|
||||
)
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class KanbanTask(
|
||||
val id: Int = 0,
|
||||
val columnId: Int = 0,
|
||||
val title: String = "",
|
||||
val description: String? = null,
|
||||
val position: Int = 0,
|
||||
val column: KanbanTaskColumn? = null
|
||||
)
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class KanbanTaskColumn(
|
||||
val board: KanbanBoard = KanbanBoard()
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class KanbanTitleRequest(@SerializedName("title") val title: String)
|
||||
|
||||
@Keep
|
||||
data class KanbanTaskRequest(
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("description") val description: String? = null
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class KanbanMoveTaskRequest(@SerializedName("columnId") val columnId: Int)
|
||||
|
||||
@Keep
|
||||
data class SendMessageRequest(
|
||||
@SerializedName("text") val text: String,
|
||||
@SerializedName("kanbanBoardId") val kanbanBoardId: Int? = null,
|
||||
@SerializedName("kanbanTaskId") val kanbanTaskId: Int? = null
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
data class LocalAccount(val id: Long, val isCurrent: Boolean)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class LoginAvailability(
|
||||
@Keep val available: Boolean
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class Message(
|
||||
@Keep var id: Int = 0,
|
||||
@Keep val senderId: Long = 0,
|
||||
@Keep val chatId: Long = 0,
|
||||
@Keep val text: String? = null,
|
||||
@Keep val sendTime: Long = 0,
|
||||
@Keep var isRead: Boolean = false,
|
||||
@Keep @SerializedName(value = "files", alternate = ["attachments"])
|
||||
val attachments: Array<Attachment> = emptyArray(),
|
||||
@Keep val kanbanBoardId: Int? = null,
|
||||
@Keep val kanbanTaskId: Int? = null,
|
||||
@Keep val kanbanBoard: KanbanBoard? = null,
|
||||
@Keep val kanbanTask: KanbanTask? = null
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Message
|
||||
|
||||
if (id != other.id) return false
|
||||
if (senderId != other.senderId) return false
|
||||
if (chatId != other.chatId) return false
|
||||
if (sendTime != other.sendTime) return false
|
||||
if (isRead != other.isRead) return false
|
||||
if (text != other.text) return false
|
||||
if (!attachments.contentEquals(other.attachments)) return false
|
||||
if (kanbanBoardId != other.kanbanBoardId) return false
|
||||
if (kanbanTaskId != other.kanbanTaskId) return false
|
||||
if (kanbanBoard != other.kanbanBoard) return false
|
||||
if (kanbanTask != other.kanbanTask) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id
|
||||
result = 31 * result + senderId.hashCode()
|
||||
result = 31 * result + chatId.hashCode()
|
||||
result = 31 * result + sendTime.hashCode()
|
||||
result = 31 * result + isRead.hashCode()
|
||||
result = 31 * result + (text?.hashCode() ?: 0)
|
||||
result = 31 * result + attachments.contentHashCode()
|
||||
result = 31 * result + (kanbanBoardId ?: 0)
|
||||
result = 31 * result + (kanbanTaskId ?: 0)
|
||||
result = 31 * result + (kanbanBoard?.hashCode() ?: 0)
|
||||
result = 31 * result + (kanbanTask?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
data class NavigationIcon(
|
||||
val icon: ImageVector,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
data class Notification(
|
||||
val chatId: Long,
|
||||
val title: String,
|
||||
val message: String
|
||||
)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
data class NotificationChannelInfo(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val description: String
|
||||
)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class NotificationTokenRequest (
|
||||
@Keep val token: String
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.aiwazian.messenger.enums.PrivacyLevel
|
||||
|
||||
@Keep
|
||||
data class PrivacySettings(
|
||||
@Keep val bio: Int = PrivacyLevel.Everybody.ordinal,
|
||||
@Keep val dateOfBirth: Int = PrivacyLevel.Everybody.ordinal,
|
||||
@Keep val lastSeen: Int = PrivacyLevel.Everybody.ordinal,
|
||||
@Keep val messages: Int = PrivacyLevel.Everybody.ordinal,
|
||||
@Keep val invites: Int = PrivacyLevel.Everybody.ordinal
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReadMessagePayload(
|
||||
val chatId: Long,
|
||||
val messageId: Int
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class RegisterRequest (
|
||||
@Keep val login: String,
|
||||
@Keep val password: String,
|
||||
@Keep val firstName: String,
|
||||
@Keep val lastName: String? = null,
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
data class ScreenEntry(
|
||||
val content: @Composable () -> Unit,
|
||||
val canGoBackBySwipe: Boolean = true
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class SearchInfo(
|
||||
@Keep val chatId: Long,
|
||||
@Keep val name: String,
|
||||
@Keep val publicLink: String
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class SessionInfo(
|
||||
@Keep val id: Int = 0,
|
||||
@Keep var deviceName: String = "",
|
||||
@Keep val createdAt: String = "",
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
data class TopBarAction(
|
||||
val icon: ImageVector,
|
||||
val onClick: (() -> Unit)? = null,
|
||||
val dropdownActions: List<DropdownMenuAction> = emptyList()
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.aiwazian.messenger.interfaces.Profile
|
||||
|
||||
@Keep
|
||||
data class UserInfo(
|
||||
@Keep override var id: Long = 0,
|
||||
@Keep var firstName: String = "",
|
||||
@Keep var lastName: String = "",
|
||||
@Keep var username: String? = null,
|
||||
@Keep var bio: String = "",
|
||||
@Keep var dateOfBirth: Long? = null,
|
||||
): Profile
|
||||
|
||||
@Keep
|
||||
data class UpdateProfileRequest(
|
||||
@SerializedName("firstName") val firstName: String,
|
||||
@SerializedName("lastName") val lastName: String? = null,
|
||||
@SerializedName("username") val username: String? = null,
|
||||
@SerializedName("bio") val bio: String? = null,
|
||||
@SerializedName("dateOfBirth") val dateOfBirth: Long? = null
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class UpdateUsernameRequest(
|
||||
@SerializedName("username") val username: String?
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.aiwazian.messenger.data
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.aiwazian.messenger.enums.WebSocketAction
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
data class WebSocketMessage(
|
||||
@Keep val action: WebSocketAction,
|
||||
@Keep val data: JsonObject
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.aiwazian.messenger.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.aiwazian.messenger.database.dao.AccountDao
|
||||
import com.aiwazian.messenger.database.dao.ChannelDao
|
||||
import com.aiwazian.messenger.database.dao.ChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderDao
|
||||
import com.aiwazian.messenger.database.dao.GroupDao
|
||||
import com.aiwazian.messenger.database.dao.UserDao
|
||||
import com.aiwazian.messenger.database.entity.AccountEntity
|
||||
import com.aiwazian.messenger.database.entity.AttachmentEntity
|
||||
import com.aiwazian.messenger.database.entity.ChannelEntity
|
||||
import com.aiwazian.messenger.database.entity.FolderChatEntity
|
||||
import com.aiwazian.messenger.database.entity.FolderEntity
|
||||
import com.aiwazian.messenger.database.entity.GroupEntity
|
||||
import com.aiwazian.messenger.database.entity.MessageEntity
|
||||
import com.aiwazian.messenger.database.entity.UserEntity
|
||||
|
||||
@Database(
|
||||
entities = [FolderEntity::class, FolderChatEntity::class, UserEntity::class, MessageEntity::class, ChannelEntity::class, AccountEntity::class, GroupEntity::class, AttachmentEntity::class],
|
||||
version = 7
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun folderDao(): FolderDao
|
||||
|
||||
abstract fun folderChatDao(): FolderChatDao
|
||||
|
||||
abstract fun userDao(): UserDao
|
||||
|
||||
abstract fun channelDao(): ChannelDao
|
||||
|
||||
abstract fun accountDao(): AccountDao
|
||||
|
||||
abstract fun groupDao(): GroupDao
|
||||
|
||||
abstract fun chatDao(): ChatDao
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.aiwazian.messenger.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.aiwazian.messenger.database.dao.AccountDao
|
||||
import com.aiwazian.messenger.database.dao.ChannelDao
|
||||
import com.aiwazian.messenger.database.dao.ChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderDao
|
||||
import com.aiwazian.messenger.database.dao.GroupDao
|
||||
import com.aiwazian.messenger.database.dao.UserDao
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object DatabaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
AppDatabase::class.java,
|
||||
"app_database"
|
||||
).fallbackToDestructiveMigration(true).build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideFolderDao(database: AppDatabase): FolderDao {
|
||||
return database.folderDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideFolderChatDao(database: AppDatabase): FolderChatDao {
|
||||
return database.folderChatDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideUserDao(database: AppDatabase): UserDao {
|
||||
return database.userDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideChannelDao(database: AppDatabase): ChannelDao {
|
||||
return database.channelDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideAccount(database: AppDatabase): AccountDao {
|
||||
return database.accountDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideGroup(database: AppDatabase): GroupDao {
|
||||
return database.groupDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideChat(database: AppDatabase): ChatDao {
|
||||
return database.chatDao()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.aiwazian.messenger.database.entity.AccountEntity
|
||||
|
||||
@Dao
|
||||
interface AccountDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun add(account: AccountEntity)
|
||||
|
||||
@Query("SELECT * FROM account WHERE id = :id")
|
||||
suspend fun get(id: Int): AccountEntity?
|
||||
|
||||
@Query("SELECT * FROM account WHERE isCurrent = 1")
|
||||
suspend fun getMe(): AccountEntity?
|
||||
|
||||
@Delete
|
||||
suspend fun delete(account: AccountEntity)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.aiwazian.messenger.database.entity.ChannelEntity
|
||||
import com.aiwazian.messenger.types.EntityId
|
||||
|
||||
@Dao
|
||||
interface ChannelDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(channelEntity: ChannelEntity)
|
||||
|
||||
@Query("SELECT * FROM channel")
|
||||
suspend fun getAll(): List<ChannelEntity>
|
||||
|
||||
@Query("SELECT * FROM channel WHERE id = :id")
|
||||
suspend fun get(id: Long): ChannelEntity?
|
||||
|
||||
@Update
|
||||
suspend fun update(channelEntity: ChannelEntity)
|
||||
|
||||
@Query("DELETE FROM 'channel' WHERE id = :id")
|
||||
suspend fun delete(id: Long)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.aiwazian.messenger.database.entity.AttachmentEntity
|
||||
|
||||
@Dao
|
||||
interface ChatDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun save(attachment: AttachmentEntity)
|
||||
|
||||
@Query("SELECT * FROM attachment WHERE id = :id")
|
||||
suspend fun get(id: String): AttachmentEntity
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.aiwazian.messenger.database.entity.FolderChatEntity
|
||||
import com.aiwazian.messenger.database.entity.MessageEntity
|
||||
|
||||
@Dao
|
||||
interface FolderChatDao {
|
||||
@Query("SELECT * FROM folderChat WHERE folderId = :id")
|
||||
suspend fun getAll(id: Int): List<FolderChatEntity>
|
||||
|
||||
@Query("SELECT * FROM folderChat WHERE id = :id")
|
||||
suspend fun get(id: Long): FolderChatEntity?
|
||||
|
||||
@Query("SELECT * FROM message WHERE chatId = :id")
|
||||
suspend fun getMessages(id: Long): List<MessageEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
|
||||
suspend fun insertAll(chatEntities: List<FolderChatEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
|
||||
suspend fun insert(chatEntity: FolderChatEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(folderChatEntity: FolderChatEntity)
|
||||
|
||||
@Query("DELETE FROM folderChat WHERE id = :id")
|
||||
suspend fun deleteById(id: Long)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.aiwazian.messenger.database.entity.FolderEntity
|
||||
|
||||
@Dao
|
||||
interface FolderDao {
|
||||
@Query("SELECT * FROM folder")
|
||||
suspend fun getAll(): List<FolderEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
|
||||
suspend fun insertAll(folderEntities: List<FolderEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.Companion.REPLACE)
|
||||
suspend fun insert(folderEntities: FolderEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(folderEntity: FolderEntity)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.aiwazian.messenger.database.entity.GroupEntity
|
||||
|
||||
@Dao
|
||||
interface GroupDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(groupEntity: GroupEntity)
|
||||
|
||||
@Query("SELECT * FROM 'group' WHERE id = :id")
|
||||
suspend fun get(id: Long): GroupEntity?
|
||||
|
||||
@Update
|
||||
suspend fun update(groupEntity: GroupEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun remove(groupEntity: GroupEntity)
|
||||
|
||||
@Query("DELETE FROM 'group' WHERE id = :id")
|
||||
suspend fun delete(id: Long)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.aiwazian.messenger.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.aiwazian.messenger.database.entity.UserEntity
|
||||
|
||||
@Dao
|
||||
interface UserDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(userEntity: UserEntity)
|
||||
|
||||
@Query("SELECT * FROM user WHERE id = :id")
|
||||
suspend fun get(id: Long): UserEntity?
|
||||
|
||||
@Delete
|
||||
suspend fun delete(userEntity: UserEntity)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("account")
|
||||
data class AccountEntity(@PrimaryKey val id: Long, val isCurrent: Boolean)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("attachment")
|
||||
data class AttachmentEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val messageId: Int,
|
||||
val name: String,
|
||||
val url: String,
|
||||
val size: Long
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("channel")
|
||||
data class ChannelEntity(
|
||||
@PrimaryKey val id: Long,
|
||||
var name: String,
|
||||
var bio: String = "",
|
||||
val ownerId: Long,
|
||||
val subscribers: Int,
|
||||
val removedUser: Int,
|
||||
val channelType: Int,
|
||||
val publicLink: String?,
|
||||
val isSubscribed: Boolean = false
|
||||
)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("folderChat")
|
||||
data class FolderChatEntity(
|
||||
@PrimaryKey var id: Long,
|
||||
val folderId: Int = 0,
|
||||
var chatName: String = "",
|
||||
var isPinned: Boolean = false,
|
||||
var lastMessageId: Int? = null
|
||||
)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "folder")
|
||||
data class FolderEntity(
|
||||
@PrimaryKey val id: Int,
|
||||
val folderName: String = ""
|
||||
)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("group")
|
||||
data class GroupEntity(
|
||||
@PrimaryKey val id: Long,
|
||||
val ownerId: Long,
|
||||
var name: String,
|
||||
var bio: String,
|
||||
val members: Int
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("message")
|
||||
data class MessageEntity(
|
||||
@PrimaryKey var id: Int,
|
||||
val senderId: Long,
|
||||
val chatId: Long,
|
||||
val text: String? = null,
|
||||
val sendTime: Long = 0,
|
||||
var isRead: Boolean = false
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.aiwazian.messenger.database.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("user")
|
||||
data class UserEntity(
|
||||
@PrimaryKey var id: Long,
|
||||
var firstName: String = "",
|
||||
var lastName: String = "",
|
||||
var username: String? = null,
|
||||
var bio: String = "",
|
||||
var dateOfBirth: Long? = null
|
||||
)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.LocalAccount
|
||||
import com.aiwazian.messenger.database.entity.AccountEntity
|
||||
|
||||
fun LocalAccount.toEntity(): AccountEntity {
|
||||
return AccountEntity(
|
||||
id = this.id,
|
||||
isCurrent = this.isCurrent
|
||||
)
|
||||
}
|
||||
|
||||
fun AccountEntity.toLocal(): LocalAccount {
|
||||
return LocalAccount(
|
||||
id = this.id,
|
||||
isCurrent = this.isCurrent
|
||||
)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.Attachment
|
||||
import com.aiwazian.messenger.database.entity.AttachmentEntity
|
||||
|
||||
fun Attachment.toEntity(): AttachmentEntity {
|
||||
return AttachmentEntity(
|
||||
id = this.id,
|
||||
messageId = this.messageId,
|
||||
name = this.name,
|
||||
url = this.url,
|
||||
size = this.size
|
||||
)
|
||||
}
|
||||
|
||||
fun AttachmentEntity.toModel(): Attachment {
|
||||
return Attachment(
|
||||
id = this.id,
|
||||
messageId = this.messageId,
|
||||
name = this.name,
|
||||
url = this.url,
|
||||
size = this.size
|
||||
)
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.ChannelInfo
|
||||
import com.aiwazian.messenger.database.entity.ChannelEntity
|
||||
|
||||
fun ChannelInfo.toEntity(): ChannelEntity {
|
||||
return ChannelEntity(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
bio = this.bio,
|
||||
ownerId = this.ownerId,
|
||||
subscribers = this.subscribers,
|
||||
removedUser = this.removedUser,
|
||||
channelType = this.channelType,
|
||||
publicLink = this.publicLink,
|
||||
isSubscribed = this.isSubscribed
|
||||
)
|
||||
}
|
||||
|
||||
fun ChannelEntity.toChannel(): ChannelInfo {
|
||||
return ChannelInfo(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
bio = this.bio,
|
||||
ownerId = this.ownerId,
|
||||
subscribers = this.subscribers,
|
||||
removedUser = this.removedUser,
|
||||
channelType = this.channelType,
|
||||
publicLink = this.publicLink,
|
||||
isSubscribed = this.isSubscribed
|
||||
)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.ChatInfo
|
||||
import com.aiwazian.messenger.database.entity.FolderChatEntity
|
||||
import com.aiwazian.messenger.enums.ChatType
|
||||
|
||||
fun ChatInfo.toEntity(
|
||||
folderId: Int,
|
||||
lastMessageId: Int? = null
|
||||
): FolderChatEntity {
|
||||
return FolderChatEntity(
|
||||
id = this.id,
|
||||
chatName = this.chatName,
|
||||
isPinned = this.isPinned,
|
||||
folderId = folderId,
|
||||
lastMessageId = lastMessageId
|
||||
)
|
||||
}
|
||||
|
||||
fun FolderChatEntity.toChat(): ChatInfo {
|
||||
return ChatInfo(
|
||||
id = this.id,
|
||||
chatName = this.chatName,
|
||||
isPinned = this.isPinned
|
||||
)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
import com.aiwazian.messenger.data.FolderInfo
|
||||
import com.aiwazian.messenger.database.entity.FolderEntity
|
||||
|
||||
fun FolderInfo.toEntity(): FolderEntity {
|
||||
return FolderEntity(
|
||||
id = this.id,
|
||||
folderName = this.name
|
||||
)
|
||||
}
|
||||
|
||||
fun FolderEntity.toFolder(): FolderInfo {
|
||||
return FolderInfo(
|
||||
id = this.id,
|
||||
name = this.folderName
|
||||
)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.GroupInfo
|
||||
import com.aiwazian.messenger.database.entity.GroupEntity
|
||||
|
||||
fun GroupInfo.toEntity(): GroupEntity {
|
||||
return GroupEntity(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
bio = this.bio,
|
||||
ownerId = this.ownerId,
|
||||
members = this.members
|
||||
)
|
||||
}
|
||||
|
||||
fun GroupEntity.toGroup(): GroupInfo {
|
||||
return GroupInfo(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
bio = this.bio,
|
||||
ownerId = this.ownerId,
|
||||
members = this.members
|
||||
)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.Message
|
||||
import com.aiwazian.messenger.database.entity.MessageEntity
|
||||
|
||||
fun Message.toEntity(): MessageEntity {
|
||||
return MessageEntity(
|
||||
id = this.id,
|
||||
senderId = this.senderId,
|
||||
chatId = this.chatId,
|
||||
text = this.text,
|
||||
sendTime = this.sendTime,
|
||||
isRead = this.isRead
|
||||
)
|
||||
}
|
||||
|
||||
fun MessageEntity.toMessage(): Message {
|
||||
return Message(
|
||||
id = this.id,
|
||||
senderId = this.senderId,
|
||||
chatId = this.chatId,
|
||||
text = this.text,
|
||||
sendTime = this.sendTime,
|
||||
isRead = this.isRead
|
||||
)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.aiwazian.messenger.database.mappers
|
||||
|
||||
import com.aiwazian.messenger.data.UserInfo
|
||||
import com.aiwazian.messenger.database.entity.UserEntity
|
||||
|
||||
fun UserInfo.toEntity(): UserEntity {
|
||||
return UserEntity(
|
||||
id = this.id,
|
||||
firstName = this.firstName,
|
||||
lastName = this.lastName,
|
||||
username = this.username,
|
||||
bio = this.bio,
|
||||
dateOfBirth = this.dateOfBirth
|
||||
)
|
||||
}
|
||||
|
||||
fun UserEntity.toUser(): UserInfo {
|
||||
return UserInfo(
|
||||
id = this.id,
|
||||
firstName = this.firstName,
|
||||
lastName = this.lastName,
|
||||
username = this.username,
|
||||
bio = this.bio,
|
||||
dateOfBirth = this.dateOfBirth
|
||||
)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import com.aiwazian.messenger.data.LocalAccount
|
||||
import com.aiwazian.messenger.database.dao.AccountDao
|
||||
import com.aiwazian.messenger.database.mappers.toLocal
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountRepository @Inject constructor(private val accountDao: AccountDao) {
|
||||
suspend fun getCurrent(): LocalAccount? {
|
||||
return accountDao.getMe()?.toLocal()
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.aiwazian.messenger.data.ChannelInfo
|
||||
import com.aiwazian.messenger.data.UserInfo
|
||||
import com.aiwazian.messenger.database.dao.ChannelDao
|
||||
import com.aiwazian.messenger.database.mappers.toChannel
|
||||
import com.aiwazian.messenger.database.mappers.toEntity
|
||||
import com.aiwazian.messenger.services.ChannelService
|
||||
import com.aiwazian.messenger.types.EntityId
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChannelRepository @Inject constructor(
|
||||
private val channelService: ChannelService,
|
||||
private val channelDao: ChannelDao
|
||||
) {
|
||||
|
||||
suspend fun get(id: Long): ChannelInfo? {
|
||||
try {
|
||||
val channel = channelService.get(id)
|
||||
|
||||
if (channel != null) {
|
||||
channelDao.insert(channel.toEntity())
|
||||
return channel
|
||||
}
|
||||
|
||||
return channelDao.get(id)?.toChannel()
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при получении канала",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val localChannel = channelDao.get(id)
|
||||
|
||||
return localChannel?.toChannel()
|
||||
}
|
||||
|
||||
suspend fun create(channelInfo: ChannelInfo): Long? {
|
||||
try {
|
||||
val createdId = channelService.create(channelInfo)
|
||||
|
||||
if (createdId == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
channelDao.insert(channelInfo.toEntity())
|
||||
|
||||
return createdId
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при создании канала",
|
||||
e
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun save(channelInfo: ChannelInfo): Long? {
|
||||
try {
|
||||
val savedId = channelService.save(channelInfo)
|
||||
|
||||
if (savedId == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
channelDao.insert(channelInfo.toEntity())
|
||||
|
||||
return savedId
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при сохранении канала",
|
||||
e
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun delete(id: Long): Boolean {
|
||||
try {
|
||||
val isDeleted = channelService.delete(id)
|
||||
|
||||
if (isDeleted) {
|
||||
channelDao.delete(id)
|
||||
}
|
||||
|
||||
return isDeleted
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при удалении канала",
|
||||
e
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSubscribers(id: Long): List<UserInfo> {
|
||||
try {
|
||||
val subscribers = channelService.getSubscribers(id)
|
||||
|
||||
return subscribers ?: emptyList()
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при получении подписчиков канала",
|
||||
e
|
||||
)
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun join(id: Long): Boolean {
|
||||
try {
|
||||
channelService.join(id)
|
||||
|
||||
val channel = channelDao.get(id)
|
||||
|
||||
if (channel != null) {
|
||||
channelDao.update(channel.copy(isSubscribed = true))
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при подписке на канал",
|
||||
e
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkIsBusyPublicLink(link: String): Boolean? {
|
||||
return try {
|
||||
channelService.isBusyPublicLick(link)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при проверке публичной ссылки канала",
|
||||
e
|
||||
)
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun leave(id: Long): Boolean {
|
||||
try {
|
||||
val channel = channelDao.get(id)
|
||||
|
||||
if (channel != null) {
|
||||
channelDao.update(channel.copy(isSubscribed = false))
|
||||
}
|
||||
|
||||
channelService.leave(id)
|
||||
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChannelRepository",
|
||||
"Ошибка при отписке от канала",
|
||||
e
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.aiwazian.messenger.data.Attachment
|
||||
import com.aiwazian.messenger.data.ChatInfo
|
||||
import com.aiwazian.messenger.data.Message
|
||||
import com.aiwazian.messenger.database.dao.ChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderChatDao
|
||||
import com.aiwazian.messenger.database.mappers.toChat
|
||||
import com.aiwazian.messenger.database.mappers.toEntity
|
||||
import com.aiwazian.messenger.database.mappers.toMessage
|
||||
import com.aiwazian.messenger.database.mappers.toModel
|
||||
import com.aiwazian.messenger.services.ChatService
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChatRepository @Inject constructor(
|
||||
private val chatService: ChatService,
|
||||
private val folderChatDao: FolderChatDao,
|
||||
private val chatDao: ChatDao
|
||||
) {
|
||||
|
||||
suspend fun get(id: Long): ChatInfo? {
|
||||
try {
|
||||
val chat = chatService.getChatInfo(id)
|
||||
|
||||
if (chat != null) {
|
||||
return chat
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChatRepository",
|
||||
"Ошибка при получении информации о чате",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val localChat = folderChatDao.get(id)
|
||||
|
||||
return localChat?.toChat()
|
||||
}
|
||||
|
||||
suspend fun getMessages(id: Long): List<Message> {
|
||||
try {
|
||||
val messages = chatService.getChatMessages(id)
|
||||
|
||||
if (messages != null) {
|
||||
return messages
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChatRepository",
|
||||
"Ошибка при получении сообщений",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val localMessages = folderChatDao.getMessages(id)
|
||||
|
||||
if (localMessages.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return localMessages.map { it.toMessage() }
|
||||
}
|
||||
|
||||
suspend fun getLastMessage(id: Long): Message? {
|
||||
return chatService.getChatLastMessage(id)
|
||||
}
|
||||
|
||||
suspend fun sendMessage(chatId: Long, message: Message): Message? {
|
||||
return chatService.sendMessage(chatId, message)
|
||||
}
|
||||
|
||||
suspend fun saveAttachment(attachment: Attachment) {
|
||||
chatDao.save(attachment.toEntity())
|
||||
}
|
||||
|
||||
suspend fun getAttachment(id: String): Attachment {
|
||||
return chatDao.get(id).toModel()
|
||||
}
|
||||
|
||||
suspend fun makeAsRead(
|
||||
chatId: Long,
|
||||
messageId: Int
|
||||
): Boolean {
|
||||
return chatService.makeAsReadMessage(
|
||||
chatId,
|
||||
messageId
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(
|
||||
chatId: Long,
|
||||
messageId: Int,
|
||||
deleteForAll: Boolean
|
||||
): Boolean {
|
||||
try {
|
||||
return chatService.deleteMessage(
|
||||
chatId,
|
||||
messageId,
|
||||
deleteForAll
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChatRepository",
|
||||
"Ошибка при удалени сообщения",
|
||||
e
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteChat(
|
||||
chatId: Long
|
||||
) {
|
||||
try {
|
||||
folderChatDao.deleteById(chatId)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"ChatRepository",
|
||||
"Ошибка при удалении чата",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteChatMessages(
|
||||
chatId: Long,
|
||||
deleteForReceiver: Boolean
|
||||
): Boolean {
|
||||
return chatService.deleteChatMessages(
|
||||
chatId,
|
||||
deleteForReceiver
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun pin(
|
||||
chatId: Long,
|
||||
folderId: Int
|
||||
): Boolean {
|
||||
return chatService.pin(
|
||||
chatId,
|
||||
folderId
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun unpin(
|
||||
chatId: Long,
|
||||
folderId: Int
|
||||
): Boolean {
|
||||
return chatService.unpin(
|
||||
chatId,
|
||||
folderId
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun archive(id: Long): Boolean {
|
||||
return chatService.archiveChat(id)
|
||||
}
|
||||
|
||||
suspend fun unarchive(id: Long): Boolean {
|
||||
return chatService.unarchiveChat(id)
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.ChatInfo
|
||||
import com.aiwazian.messenger.data.FolderInfo
|
||||
import com.aiwazian.messenger.database.dao.FolderChatDao
|
||||
import com.aiwazian.messenger.database.dao.FolderDao
|
||||
import com.aiwazian.messenger.database.mappers.toEntity
|
||||
import com.aiwazian.messenger.database.mappers.toChat
|
||||
import com.aiwazian.messenger.database.mappers.toFolder
|
||||
import com.aiwazian.messenger.services.FolderService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FolderRepository @Inject constructor(
|
||||
private val folderService: FolderService,
|
||||
private val folderDao: FolderDao,
|
||||
private val folderChatDao: FolderChatDao
|
||||
) {
|
||||
|
||||
private val _folders = MutableStateFlow<List<FolderInfo>>(emptyList())
|
||||
val folders = _folders.asStateFlow()
|
||||
|
||||
suspend fun loadFolders() {
|
||||
val localFolderEntities = folderDao.getAll()
|
||||
|
||||
val localFolders = localFolderEntities.map { it.toFolder() }
|
||||
|
||||
_folders.update {
|
||||
localFolders.map { folder ->
|
||||
folder.chats = folderChatDao.getAll(folder.id).map { it.toChat() }
|
||||
folder
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val folders = folderService.getAll().orEmpty()
|
||||
val chatsResponse = RetrofitInstance.api.getUnarchivedChats()
|
||||
if (!chatsResponse.isSuccessful) return
|
||||
val response = chatsResponse.body().orEmpty()
|
||||
|
||||
val chatFolderInfos = listOf(
|
||||
FolderInfo(
|
||||
id = 0,
|
||||
name = "Все чаты",
|
||||
chats = response
|
||||
)
|
||||
) + folders
|
||||
|
||||
_folders.update { chatFolderInfos }
|
||||
|
||||
val folderEntities = _folders.value.map { it.toEntity() }
|
||||
folderDao.insertAll(folderEntities)
|
||||
|
||||
_folders.value.forEach { folder ->
|
||||
val chatEntities = folder.chats.map { it.toEntity(folder.id) }
|
||||
folderChatDao.insertAll(chatEntities)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"FolderRepository",
|
||||
"Ошибка при получении папок с чатами",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getFolderChats(folderId: Int): List<ChatInfo> {
|
||||
return _folders.value.find { it.id == folderId }?.chats ?: emptyList()
|
||||
}
|
||||
|
||||
suspend fun saveFolder(folderInfo: FolderInfo) {
|
||||
if (folderInfo.id == 0) {
|
||||
folderInfo.id = (_folders.value.maxOfOrNull { it.id } ?: 0) + 1
|
||||
}
|
||||
|
||||
_folders.update { currentFolders ->
|
||||
val existingIndex = currentFolders.indexOfFirst { it.id == folderInfo.id }
|
||||
|
||||
if (existingIndex != -1) {
|
||||
currentFolders.toMutableList().apply {
|
||||
this[existingIndex] = folderInfo
|
||||
}
|
||||
} else {
|
||||
currentFolders + folderInfo
|
||||
}
|
||||
}
|
||||
folderDao.insertAll(listOf(folderInfo.toEntity()))
|
||||
folderChatDao.insertAll(folderInfo.chats.map { it.toEntity(folderInfo.id) })
|
||||
}
|
||||
|
||||
suspend fun remove(folderId: Int): Boolean {
|
||||
val folder = _folders.value.find { it.id == folderId }?.toEntity()
|
||||
|
||||
if (folder == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
_folders.update { it.filter { it -> it.id != folderId } }
|
||||
|
||||
folderDao.delete(folder)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.aiwazian.messenger.data.GroupInfo
|
||||
import com.aiwazian.messenger.database.dao.GroupDao
|
||||
import com.aiwazian.messenger.database.mappers.toEntity
|
||||
import com.aiwazian.messenger.database.mappers.toGroup
|
||||
import com.aiwazian.messenger.services.GroupService
|
||||
import javax.inject.Inject
|
||||
|
||||
class GroupRepository @Inject constructor(
|
||||
private val groupService: GroupService,
|
||||
private val groupDao: GroupDao
|
||||
) {
|
||||
|
||||
suspend fun create(groupInfo: GroupInfo): Long? {
|
||||
try {
|
||||
val createdId = groupService.create(groupInfo)
|
||||
|
||||
if (createdId == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
groupDao.insert(groupInfo.toEntity())
|
||||
|
||||
return createdId
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"GroupRepository",
|
||||
"Ошибка при создании канала",
|
||||
e
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun get(id: Long): GroupInfo? {
|
||||
try {
|
||||
val group = groupService.get(id)
|
||||
|
||||
if (group != null) {
|
||||
groupDao.insert(group.toEntity())
|
||||
return group
|
||||
}
|
||||
|
||||
return groupDao.get(id)?.toGroup()
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"GroupRepository",
|
||||
"Ошибка при получении группы",
|
||||
e
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun delete(id: Long): Boolean {
|
||||
try {
|
||||
val isDeleted = groupService.delete(id)
|
||||
|
||||
if (isDeleted) {
|
||||
groupDao.delete(id)
|
||||
}
|
||||
|
||||
return isDeleted
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"GroupRepository",
|
||||
"Ошибка при получении участников группы",
|
||||
e
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.aiwazian.messenger.database.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.UserInfo
|
||||
import com.aiwazian.messenger.database.dao.AccountDao
|
||||
import com.aiwazian.messenger.database.dao.UserDao
|
||||
import com.aiwazian.messenger.database.entity.AccountEntity
|
||||
import com.aiwazian.messenger.database.mappers.toEntity
|
||||
import com.aiwazian.messenger.database.mappers.toUser
|
||||
import com.aiwazian.messenger.services.UserService
|
||||
import javax.inject.Inject
|
||||
|
||||
class UserRepository @Inject constructor(
|
||||
private val userService: UserService,
|
||||
private val userDao: UserDao,
|
||||
private val accountDao: AccountDao
|
||||
) {
|
||||
|
||||
suspend fun getMe(): UserInfo? {
|
||||
try {
|
||||
val response = RetrofitInstance.api.getMe()
|
||||
|
||||
val user = response.body()
|
||||
|
||||
if (user != null) {
|
||||
val userEntity = user.toEntity()
|
||||
userDao.insert(userEntity)
|
||||
|
||||
val accountEntity = AccountEntity(id = userEntity.id, isCurrent = true)
|
||||
accountDao.add(accountEntity)
|
||||
|
||||
return user
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"UserRepository",
|
||||
"Ошибка при запросе Get Me",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val accountEntity = accountDao.getMe()
|
||||
|
||||
if (accountEntity == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val user = userDao.get(accountEntity.id)
|
||||
|
||||
return user?.toUser()
|
||||
}
|
||||
|
||||
suspend fun getById(id: Long): UserInfo? {
|
||||
try {
|
||||
val user = userService.getById(id)
|
||||
|
||||
if (user != null) {
|
||||
userDao.insert(user.toEntity())
|
||||
return user
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"UserRepository",
|
||||
"Ошибка при получении профиля",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val localUser = userDao.get(id)
|
||||
|
||||
return localUser?.toUser()
|
||||
}
|
||||
|
||||
suspend fun updateProfile(user: UserInfo): Boolean {
|
||||
try {
|
||||
userDao.insert(user.toEntity())
|
||||
return userService.updateProfile(user)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"UserRepository",
|
||||
"Ошибка при обновлении профиля",
|
||||
e
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class ChannelType {
|
||||
PUBLIC,
|
||||
PRIVATE;
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int): ChannelType {
|
||||
return entries.first { it.ordinal == value }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class ChatType {
|
||||
PRIVATE,
|
||||
GROUP,
|
||||
CHANNEL,
|
||||
UNKNOWN;
|
||||
|
||||
companion object {
|
||||
fun fromOrdinal(ordinal: Int): ChatType {
|
||||
return entries.firstOrNull { it.ordinal == ordinal } ?: UNKNOWN
|
||||
}
|
||||
|
||||
fun fromId(id: Long): ChatType {
|
||||
val idString = id.toString()
|
||||
val firstDigit = idString[0].digitToInt()
|
||||
|
||||
return when (firstDigit) {
|
||||
1-> PRIVATE
|
||||
2-> CHANNEL
|
||||
3-> GROUP
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class DownloadStatus {
|
||||
PENDING,
|
||||
DOWNLOADING,
|
||||
COMPLETED
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class FileType {
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
MUSIC,
|
||||
ZIP,
|
||||
TEXT,
|
||||
HTML,
|
||||
CSS,
|
||||
JAVASCRIPT,
|
||||
PHP,
|
||||
APK,
|
||||
GIF,
|
||||
JSON,
|
||||
OTHER;
|
||||
|
||||
companion object {
|
||||
private val extensionMap: Map<String, FileType> = mapOf(
|
||||
"jpg" to IMAGE,
|
||||
"jpeg" to IMAGE,
|
||||
"png" to IMAGE,
|
||||
"gif" to IMAGE,
|
||||
"bmp" to IMAGE,
|
||||
"mp4" to VIDEO,
|
||||
"avi" to VIDEO,
|
||||
"mkv" to VIDEO,
|
||||
"mov" to VIDEO,
|
||||
"mp3" to MUSIC,
|
||||
"wav" to MUSIC,
|
||||
"aac" to MUSIC,
|
||||
"flac" to MUSIC,
|
||||
"zip" to ZIP,
|
||||
"txt" to TEXT,
|
||||
"html" to HTML,
|
||||
"css" to CSS,
|
||||
"js" to JAVASCRIPT,
|
||||
"php" to PHP,
|
||||
"gif" to GIF,
|
||||
"apk" to APK,
|
||||
"json" to JSON
|
||||
)
|
||||
|
||||
fun fromExtension(extension: String): FileType {
|
||||
return extensionMap[extension.lowercase()] ?: OTHER
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
enum class PrimaryColorOption(val color: Color) {
|
||||
Blue(Color(0xFF2196F3)),
|
||||
Green(Color(0xFF4CAF50)),
|
||||
DarkGreen(Color(0xFF009688)),
|
||||
Purple(Color(0xFF9C27B0)),
|
||||
Orange(Color(0xFFFF5722)),
|
||||
Orange1(Color(0xFFE91E63)),
|
||||
Pink(Color(0xFFFF00FF)),
|
||||
Pink1(Color(0xFF673AB7));
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String): PrimaryColorOption {
|
||||
return entries.firstOrNull { it.name.equals(value, ignoreCase = true) } ?: Blue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class PrivacyLevel(val id: Int) {
|
||||
Everybody(0),
|
||||
Nobody(1);
|
||||
|
||||
companion object {
|
||||
fun fromId(id: Int): PrivacyLevel {
|
||||
return entries.first { it.id == id }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
enum class ThemeOption {
|
||||
LIGHT,
|
||||
DARK,
|
||||
SYSTEM;
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String): ThemeOption {
|
||||
return entries.firstOrNull {
|
||||
it.name.equals(
|
||||
value,
|
||||
ignoreCase = true
|
||||
)
|
||||
} ?: SYSTEM
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.aiwazian.messenger.enums
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class WebSocketAction {
|
||||
@SerialName("NEW_MESSAGE")
|
||||
NEW_MESSAGE,
|
||||
|
||||
@SerialName("DELETE_MESSAGE")
|
||||
DELETE_MESSAGE,
|
||||
|
||||
@SerialName("DELETE_CHAT")
|
||||
DELETE_CHAT,
|
||||
|
||||
@SerialName("READ_MESSAGE")
|
||||
READ_MESSAGE,
|
||||
|
||||
@SerialName("NEW_CHAT")
|
||||
NEW_CHAT,
|
||||
|
||||
@SerialName("HISTORY_CLEAR")
|
||||
HISTORY_CLEAR,
|
||||
|
||||
KANBAN_UPDATE,
|
||||
|
||||
UNKNOWN
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package com.aiwazian.messenger.interfaces
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.aiwazian.messenger.data.ApiResponse
|
||||
import com.aiwazian.messenger.data.AuthRequest
|
||||
import com.aiwazian.messenger.data.FileDownloadUrlResponse
|
||||
import com.aiwazian.messenger.data.AuthResponse
|
||||
import com.aiwazian.messenger.data.ChangeCloudPasswordRequest
|
||||
import com.aiwazian.messenger.data.ChannelInfo
|
||||
import com.aiwazian.messenger.data.CreateChannelRequest
|
||||
import com.aiwazian.messenger.data.CreateGroupRequest
|
||||
import com.aiwazian.messenger.data.CreatedEntityResponse
|
||||
import com.aiwazian.messenger.data.ChatInfo
|
||||
import com.aiwazian.messenger.data.FolderInfo
|
||||
import com.aiwazian.messenger.data.FileUploadConfirmRequest
|
||||
import com.aiwazian.messenger.data.FileUploadInitRequest
|
||||
import com.aiwazian.messenger.data.FileUploadInitResponse
|
||||
import com.aiwazian.messenger.data.GroupInfo
|
||||
import com.aiwazian.messenger.data.LoginAvailability
|
||||
import com.aiwazian.messenger.data.KanbanBoard
|
||||
import com.aiwazian.messenger.data.KanbanMoveTaskRequest
|
||||
import com.aiwazian.messenger.data.KanbanTaskRequest
|
||||
import com.aiwazian.messenger.data.KanbanTitleRequest
|
||||
import com.aiwazian.messenger.data.Message
|
||||
import com.aiwazian.messenger.data.NotificationTokenRequest
|
||||
import com.aiwazian.messenger.data.PrivacySettings
|
||||
import com.aiwazian.messenger.data.RegisterRequest
|
||||
import com.aiwazian.messenger.data.SearchInfo
|
||||
import com.aiwazian.messenger.data.SessionInfo
|
||||
import com.aiwazian.messenger.data.SendMessageRequest
|
||||
import com.aiwazian.messenger.data.UserInfo
|
||||
import com.aiwazian.messenger.data.UsernameAvailability
|
||||
import com.aiwazian.messenger.data.UpdateProfileRequest
|
||||
import com.aiwazian.messenger.data.UpdateUsernameRequest
|
||||
import com.aiwazian.messenger.utils.Route
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.PATCH
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
|
||||
@Keep
|
||||
interface ApiService {
|
||||
@GET("api/kanban")
|
||||
suspend fun getKanbanBoards(): Response<List<KanbanBoard>>
|
||||
|
||||
@POST("api/kanban")
|
||||
suspend fun createKanbanBoard(@Body request: KanbanTitleRequest): Response<KanbanBoard>
|
||||
|
||||
@PATCH("api/kanban/{boardId}")
|
||||
suspend fun renameKanbanBoard(
|
||||
@Path("boardId") boardId: Int,
|
||||
@Body request: KanbanTitleRequest
|
||||
): Response<KanbanBoard>
|
||||
|
||||
@DELETE("api/kanban/{boardId}")
|
||||
suspend fun deleteKanbanBoard(@Path("boardId") boardId: Int): Response<Unit>
|
||||
|
||||
@POST("api/kanban/{boardId}/columns")
|
||||
suspend fun createKanbanColumn(@Path("boardId") boardId: Int, @Body request: KanbanTitleRequest): Response<KanbanBoard>
|
||||
|
||||
@POST("api/kanban/columns/{columnId}/tasks")
|
||||
suspend fun createKanbanTask(@Path("columnId") columnId: Int, @Body request: KanbanTaskRequest): Response<KanbanBoard>
|
||||
|
||||
@PATCH("api/kanban/tasks/{taskId}")
|
||||
suspend fun moveKanbanTask(@Path("taskId") taskId: Int, @Body request: KanbanMoveTaskRequest): Response<KanbanBoard>
|
||||
|
||||
@DELETE("api/kanban/tasks/{taskId}")
|
||||
suspend fun deleteKanbanTask(@Path("taskId") taskId: Int): Response<KanbanBoard>
|
||||
|
||||
@GET(Route.FIND_USER_BY_LOGIN)
|
||||
suspend fun findUserByLogin(@Path("login") login: String): Response<LoginAvailability>
|
||||
|
||||
@POST(Route.LOGIN)
|
||||
suspend fun login(@Body request: AuthRequest): Response<AuthResponse>
|
||||
|
||||
@POST(Route.REGISTER)
|
||||
suspend fun register(@Body request: RegisterRequest): Response<Unit>
|
||||
|
||||
@POST(Route.LOGOUT)
|
||||
suspend fun logout(): Response<ApiResponse>
|
||||
|
||||
@GET(Route.ME)
|
||||
suspend fun getMe(): Response<UserInfo>
|
||||
|
||||
@GET(Route.UNARCHIVED_CHATS)
|
||||
suspend fun getUnarchivedChats(): Response<List<ChatInfo>>
|
||||
|
||||
@GET(Route.ARCHIVED_CHATS)
|
||||
suspend fun getArchivedChats(): Response<List<ChatInfo>>
|
||||
|
||||
@GET(Route.GET_SESSIONS)
|
||||
suspend fun getSessions(): Response<List<SessionInfo>>
|
||||
|
||||
@POST(Route.UPDATE_FCM_TOKEN)
|
||||
suspend fun updateFcmToken(@Body newToken: NotificationTokenRequest): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.TERMINATE_ALL_SESSIONS)
|
||||
suspend fun terminateAllSessions(): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.TERMINATE_SESSION)
|
||||
suspend fun terminateSession(@Path("id") id: Int): Response<ApiResponse>
|
||||
|
||||
@GET(Route.GET_DEVICE_COUNT)
|
||||
suspend fun getDeviceCount(): Response<Int>
|
||||
|
||||
@DELETE(Route.DELETE_CHAT)
|
||||
suspend fun deleteChat(
|
||||
@Path("id") chatId: Long,
|
||||
@Query("deleteForReceiver") deleteForReceiver: Boolean
|
||||
): Response<Unit>
|
||||
|
||||
@DELETE(Route.DELETE_CHAT_MESSAGES)
|
||||
suspend fun deleteChatMessages(
|
||||
@Path("id") id: Long,
|
||||
@Query("deleteForReceiver") deleteForReceiver: Boolean
|
||||
): Response<Unit>
|
||||
|
||||
@PATCH(Route.CHANGE_CLOUD_PASSWORD)
|
||||
suspend fun changeCloudPassword(@Body body: ChangeCloudPasswordRequest): Response<ApiResponse>
|
||||
|
||||
@PATCH(Route.CHANGE_BIO_PRIVACY)
|
||||
suspend fun changeBioPrivacy(@Path("value") body: Int): Response<ApiResponse>
|
||||
|
||||
@PATCH(Route.CHANGE_DATE_OF_BIRTH_PRIVACY)
|
||||
suspend fun changeDateOfBirthPrivacy(@Path("value") body: Int): Response<ApiResponse>
|
||||
|
||||
@GET(Route.CHAT_MESSAGES)
|
||||
suspend fun getMessagesBetweenUsers(@Path("id") chatId: Long): Response<List<Message>>
|
||||
|
||||
@GET(Route.GET_CHAT_LAST_MESSAGE)
|
||||
suspend fun getChatLastMessage(@Path("chatId") chatId: Long): Response<Message>
|
||||
|
||||
@GET(Route.GET_CHAT_INFO)
|
||||
suspend fun getChatInfo(@Path("id") id: Long): Response<ChatInfo?>
|
||||
|
||||
@PATCH("api/users/me")
|
||||
suspend fun updateProfile(@Body profile: UpdateProfileRequest): Response<UserInfo>
|
||||
|
||||
@GET("api/search")
|
||||
suspend fun searchUser(@Query("q") query: String): Response<List<SearchInfo>>
|
||||
|
||||
@GET(Route.GE_USER_BY_ID)
|
||||
suspend fun getUserById(@Path("id") id: Long): Response<UserInfo>
|
||||
|
||||
@POST(Route.ADD_CHAT_TO_ARCHIVE)
|
||||
suspend fun archiveChat(@Path("id") chatId: Long): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.DELETE_CHAT_FROM_ARCHIVE)
|
||||
suspend fun unarchiveChat(@Path("id") chatId: Long): Response<ApiResponse>
|
||||
|
||||
@POST(Route.SEND_MESSAGE)
|
||||
suspend fun sendMessage(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Body requestBody: SendMessageRequest
|
||||
): Response<Message>
|
||||
|
||||
@Multipart
|
||||
@POST(Route.SEND_DOCUMENT)
|
||||
suspend fun sendDocument(
|
||||
@Part file: MultipartBody.Part,
|
||||
@Path("chatId") chatId: Long
|
||||
): Response<Message>
|
||||
|
||||
@POST("api/chats/{chatId}/messages/files/init")
|
||||
suspend fun initFileUpload(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Body request: FileUploadInitRequest
|
||||
): Response<FileUploadInitResponse>
|
||||
|
||||
@POST("api/chats/{chatId}/messages/files/confirm")
|
||||
suspend fun confirmFileUpload(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Body request: FileUploadConfirmRequest
|
||||
): Response<Message>
|
||||
|
||||
@GET("api/chats/{chatId}/messages/{messageId}/files/{fileId}/download")
|
||||
suspend fun getFileDownloadUrl(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Path("messageId") messageId: Int,
|
||||
@Path("fileId") fileId: String
|
||||
): Response<FileDownloadUrlResponse>
|
||||
|
||||
@DELETE(Route.DELETE_MESSAGE)
|
||||
suspend fun deleteMessage(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Path("messageId") messageId: Int,
|
||||
@Query("forEveryone") forEveryone: Boolean
|
||||
): Response<Unit>
|
||||
|
||||
@POST(Route.MAKE_AS_READ_MESSAGE)
|
||||
suspend fun makeAsReadMessage(
|
||||
@Path("chatId") chatId: Long,
|
||||
@Path("messageId") messageId: Int
|
||||
): Response<Unit>
|
||||
|
||||
@POST(Route.FOLDER)
|
||||
suspend fun saveFolder(@Body requestBody: FolderInfo): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.DELETE_FOLDER)
|
||||
suspend fun deleteFolder(@Path("id") id: Int): Response<ApiResponse>
|
||||
|
||||
@GET(Route.FOLDERS)
|
||||
suspend fun getFolders(): Response<List<FolderInfo>>
|
||||
|
||||
@GET(Route.CHATS)
|
||||
suspend fun getAllChats(): Response<List<ChatInfo>>
|
||||
|
||||
@GET(Route.CHATS)
|
||||
suspend fun getAllChatsWithOtherUser(): Response<List<ChatInfo>>
|
||||
|
||||
@POST(Route.PIN_CHAT)
|
||||
suspend fun pinChat(
|
||||
@Path("id") chatId: Long
|
||||
): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.UNPIN_CHAT)
|
||||
suspend fun unpinChat(
|
||||
@Path("id") chatId: Long
|
||||
): Response<ApiResponse>
|
||||
|
||||
@POST(Route.PIN_CHAT_IN_FOLDER)
|
||||
suspend fun pinChatInFolder(
|
||||
@Path("folderId") folderId: Int,
|
||||
@Path("chatId") chatId: Long
|
||||
): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.UNPIN_CHAT_IN_FOLDER)
|
||||
suspend fun unpinChatInFolder(
|
||||
@Path("folderId") folderId: Int,
|
||||
@Path("chatId") chatId: Long
|
||||
): Response<ApiResponse>
|
||||
|
||||
@GET(Route.GET_MY_PRIVACY)
|
||||
suspend fun getMyPrivacy(): Response<PrivacySettings>
|
||||
|
||||
@PATCH("api/users/me/privacy")
|
||||
suspend fun updatePrivacy(@Body settings: PrivacySettings): Response<PrivacySettings>
|
||||
|
||||
@GET("api/search/check/{username}")
|
||||
suspend fun checkUsername(@Path("username") username: String): Response<UsernameAvailability>
|
||||
|
||||
@PATCH("api/users/me/username")
|
||||
suspend fun saveUsername(@Body request: UpdateUsernameRequest): Response<UserInfo>
|
||||
|
||||
@POST(Route.CREATE_CHANNEL)
|
||||
suspend fun createChannel(@Body channelInfo: CreateChannelRequest): Response<CreatedEntityResponse>
|
||||
|
||||
@POST(Route.SAVE_CHANNEL)
|
||||
suspend fun saveChannel(
|
||||
@Path("id") id: Long,
|
||||
@Body channelInfo: ChannelInfo
|
||||
): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.DELETE_CHANNEL)
|
||||
suspend fun deleteChannel(@Path("id") id: Long): Response<ApiResponse>
|
||||
|
||||
@GET(Route.GET_CHANNEL)
|
||||
suspend fun getChannel(@Path("id") id: Long): Response<ChannelInfo>
|
||||
|
||||
@POST(Route.JOIN_CHANNEL)
|
||||
suspend fun joinChannel(@Path("id") id: Long): Response<ApiResponse>
|
||||
|
||||
@DELETE(Route.LEAVE_CHANNEL)
|
||||
suspend fun leaveChannel(@Path("id") id: Long): Response<ApiResponse>
|
||||
|
||||
@GET(Route.GET_CHANNEL_SUBSCRIBERS)
|
||||
suspend fun getChannelSubscribers(@Path("id") id: Long): Response<List<UserInfo>>
|
||||
|
||||
@GET(Route.CHECK_CHANNEL_PUBLIC_LINK)
|
||||
suspend fun checkChannelPublicLink(@Path("link") link: String): Response<ApiResponse>
|
||||
|
||||
@POST(Route.CREATE_GROUP)
|
||||
suspend fun createGroup(@Body groupInfo: CreateGroupRequest): Response<CreatedEntityResponse>
|
||||
|
||||
@GET(Route.GET_GROUP)
|
||||
suspend fun getGroup(@Path("id") id: Long): Response<GroupInfo>
|
||||
|
||||
@DELETE(Route.DELETE_GROUP)
|
||||
suspend fun deleteGroup(@Path("id") id: Long): Response<ApiResponse>
|
||||
|
||||
@GET(Route.GET_GROUP_MEMBERS)
|
||||
suspend fun getGroupMembers(@Path("id") id: Long): Response<List<UserInfo>>
|
||||
|
||||
@POST(Route.INVITE_USER_TO_GROUP)
|
||||
suspend fun inviteUserToGroup(
|
||||
@Path("groupId") groupId: Long,
|
||||
@Path("userId") userId: Long
|
||||
): Response<Unit>
|
||||
|
||||
@DELETE(Route.REMOVE_USER_FROM_GROUP)
|
||||
suspend fun removeUserFromGroup(
|
||||
@Path("groupId") groupId: Long,
|
||||
@Path("userId") userId: Long
|
||||
): Response<Unit>
|
||||
|
||||
@Streaming
|
||||
@GET
|
||||
fun downloadFile(@Url fileUrl: String): Call<ResponseBody>
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.aiwazian.messenger.interfaces
|
||||
|
||||
import com.aiwazian.messenger.data.Notification
|
||||
|
||||
interface NotificationService {
|
||||
fun showNotification(
|
||||
notification: Notification,
|
||||
messages: List<String>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.aiwazian.messenger.interfaces
|
||||
|
||||
import com.google.errorprone.annotations.Keep
|
||||
|
||||
@Keep
|
||||
interface Profile {
|
||||
val id: Long
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.aiwazian.messenger.interfaces
|
||||
|
||||
interface QrCodeService {
|
||||
//fun createQrCode()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppLockService @Inject constructor() {
|
||||
|
||||
private val _isLockApp = MutableStateFlow(false)
|
||||
val isLockApp = _isLockApp.asStateFlow()
|
||||
|
||||
private val _passcode = MutableStateFlow("")
|
||||
|
||||
private val _hasPasscode = MutableStateFlow(false)
|
||||
val hasPasscode = _hasPasscode.asStateFlow()
|
||||
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
private val dataStoreManager = DataStoreManager.getInstance()
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
val passcode = dataStoreManager.getPasscode().first()
|
||||
_passcode.update { passcode }
|
||||
|
||||
_hasPasscode.update { _passcode.value.isNotBlank() }
|
||||
|
||||
val isLock = dataStoreManager.getIsLockApp().first()
|
||||
_isLockApp.update { isLock }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun lock() {
|
||||
_isLockApp.update { true }
|
||||
dataStoreManager.saveIsLockApp(true)
|
||||
}
|
||||
|
||||
suspend fun unlock() {
|
||||
_isLockApp.update { false }
|
||||
dataStoreManager.saveIsLockApp(false)
|
||||
}
|
||||
|
||||
suspend fun disablePasscode() {
|
||||
_hasPasscode.update { false }
|
||||
dataStoreManager.savePasscode("")
|
||||
}
|
||||
|
||||
suspend fun changePasscode(newPasscode: String) {
|
||||
_passcode.update { newPasscode }
|
||||
_hasPasscode.update { true }
|
||||
dataStoreManager.savePasscode(newPasscode)
|
||||
}
|
||||
|
||||
fun checkPasscode(passcode: String): Boolean {
|
||||
return passcode == _passcode.value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.AuthRequest
|
||||
import com.aiwazian.messenger.data.RegisterRequest
|
||||
import com.aiwazian.messenger.utils.WebSocketManager
|
||||
import javax.inject.Inject
|
||||
|
||||
class AuthService @Inject constructor() {
|
||||
|
||||
suspend fun logout() {
|
||||
RetrofitInstance.api.logout()
|
||||
WebSocketManager.close()
|
||||
TokenManager.setAuthorized(false)
|
||||
TokenManager.removeToken()
|
||||
}
|
||||
|
||||
suspend fun login(authRequest: AuthRequest): String? {
|
||||
val response = RetrofitInstance.api.login(authRequest)
|
||||
|
||||
return response.body()?.token
|
||||
}
|
||||
|
||||
suspend fun register(registerRequest: RegisterRequest): Boolean {
|
||||
val response = RetrofitInstance.api.register(registerRequest)
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun findUserByLogin(login: String): Boolean {
|
||||
val response = RetrofitInstance.api.findUserByLogin(login)
|
||||
return response.body()?.available == false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.ChannelInfo
|
||||
import com.aiwazian.messenger.data.CreateChannelRequest
|
||||
import com.aiwazian.messenger.enums.ChannelType
|
||||
import com.aiwazian.messenger.data.UserInfo
|
||||
import com.aiwazian.messenger.types.EntityId
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChannelService @Inject constructor() {
|
||||
|
||||
suspend fun create(channel: ChannelInfo): Long? {
|
||||
val response = RetrofitInstance.api.createChannel(
|
||||
CreateChannelRequest(
|
||||
name = channel.name,
|
||||
bio = channel.bio.ifBlank { null },
|
||||
channelType = if (channel.channelType == ChannelType.PUBLIC.ordinal) "PUBLIC" else "PRIVATE",
|
||||
username = channel.publicLink?.trim()?.trimStart('@')?.ifBlank { null }
|
||||
)
|
||||
)
|
||||
return response.body()?.id
|
||||
}
|
||||
|
||||
suspend fun save(channel: ChannelInfo): Long? {
|
||||
val response = RetrofitInstance.api.saveChannel(channel.id, channel)
|
||||
return response.body()?.message?.toLongOrNull()
|
||||
}
|
||||
|
||||
suspend fun delete(id: Long): Boolean {
|
||||
val response = RetrofitInstance.api.deleteChannel(id)
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun get(id: Long): ChannelInfo? {
|
||||
val response = RetrofitInstance.api.getChannel(id)
|
||||
return response.body()
|
||||
}
|
||||
|
||||
suspend fun join(id: Long): Boolean {
|
||||
val response = RetrofitInstance.api.joinChannel(id)
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun leave(id: Long): Boolean {
|
||||
val response = RetrofitInstance.api.leaveChannel(id)
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun isBusyPublicLick(link:String): Boolean {
|
||||
val response = RetrofitInstance.api.checkChannelPublicLink(link)
|
||||
return !response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun getSubscribers(id:Long): List<UserInfo>? {
|
||||
val response = RetrofitInstance.api.getChannelSubscribers(id)
|
||||
return response.body()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.ChatInfo
|
||||
import com.aiwazian.messenger.data.Message
|
||||
import com.aiwazian.messenger.data.SendMessageRequest
|
||||
import okhttp3.MultipartBody
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChatService @Inject constructor() {
|
||||
|
||||
suspend fun sendMessage(chatId: Long, message: Message): Message? {
|
||||
val response = RetrofitInstance.api.sendMessage(
|
||||
chatId,
|
||||
SendMessageRequest(message.text.orEmpty(), message.kanbanBoardId, message.kanbanTaskId)
|
||||
)
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun sendDocument(fileUri: MultipartBody.Part, chatId: Long): Message? {
|
||||
val response = RetrofitInstance.api.sendDocument(fileUri, chatId)
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun getChatInfo(chatId: Long): ChatInfo? {
|
||||
val response = RetrofitInstance.api.getChatInfo(chatId)
|
||||
return response.body()
|
||||
}
|
||||
|
||||
suspend fun getAllChatsWithOtherUser(): List<ChatInfo>? {
|
||||
val response = RetrofitInstance.api.getAllChatsWithOtherUser()
|
||||
return response.body()
|
||||
}
|
||||
|
||||
suspend fun makeAsReadMessage(
|
||||
chatId: Long,
|
||||
messageId: Int
|
||||
): Boolean {
|
||||
val response = RetrofitInstance.api.makeAsReadMessage(
|
||||
chatId,
|
||||
messageId
|
||||
)
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun getChatLastMessage(chatId: Long): Message? {
|
||||
return getChatMessages(chatId)?.lastOrNull()
|
||||
}
|
||||
|
||||
suspend fun getChatMessages(chatId: Long): List<Message>? {
|
||||
val response = RetrofitInstance.api.getMessagesBetweenUsers(chatId)
|
||||
return response.body()
|
||||
}
|
||||
|
||||
suspend fun archiveChat(chatId: Long): Boolean {
|
||||
val response = RetrofitInstance.api.archiveChat(chatId)
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun unarchiveChat(chatId: Long): Boolean {
|
||||
val response = RetrofitInstance.api.unarchiveChat(chatId)
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun pin(
|
||||
chatId: Long,
|
||||
folderId: Int
|
||||
): Boolean {
|
||||
val response = if (folderId == 0) {
|
||||
RetrofitInstance.api.pinChat(chatId)
|
||||
} else {
|
||||
RetrofitInstance.api.pinChatInFolder(
|
||||
folderId,
|
||||
chatId
|
||||
)
|
||||
}
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun unpin(
|
||||
chatId: Long,
|
||||
folderId: Int
|
||||
): Boolean {
|
||||
val response = if (folderId == 0) {
|
||||
RetrofitInstance.api.unpinChat(chatId)
|
||||
} else {
|
||||
RetrofitInstance.api.unpinChatInFolder(
|
||||
folderId,
|
||||
chatId
|
||||
)
|
||||
}
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(
|
||||
chatId: Long,
|
||||
messageId: Int,
|
||||
deleteForAll: Boolean
|
||||
): Boolean {
|
||||
val response = RetrofitInstance.api.deleteMessage(
|
||||
chatId,
|
||||
messageId,
|
||||
deleteForAll
|
||||
)
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun deleteChat(
|
||||
chatId: Long,
|
||||
deleteForReceiver: Boolean
|
||||
): Boolean {
|
||||
val response = RetrofitInstance.api.deleteChat(
|
||||
chatId,
|
||||
deleteForReceiver
|
||||
)
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
|
||||
suspend fun deleteChatMessages(
|
||||
chatId: Long,
|
||||
deleteForReceiver: Boolean
|
||||
): Boolean {
|
||||
val response = RetrofitInstance.api.deleteChatMessages(
|
||||
chatId,
|
||||
deleteForReceiver
|
||||
)
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
|
||||
class ClipboardHelper(private val context: Context) {
|
||||
fun copy(text: String) {
|
||||
val clipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
val clipData = ClipData.newPlainText("label", text)
|
||||
|
||||
clipboardManager.setPrimaryClip(clipData)
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import com.aiwazian.messenger.enums.PrimaryColorOption
|
||||
import com.aiwazian.messenger.enums.ThemeOption
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private const val USER_PREFERENCES_NAME = "data_store"
|
||||
|
||||
private val Context.dataStore by preferencesDataStore(USER_PREFERENCES_NAME)
|
||||
|
||||
private object Keys {
|
||||
val THEME = stringPreferencesKey("app_theme")
|
||||
val TOKEN = stringPreferencesKey("token")
|
||||
val PRIMARY_COLOR = stringPreferencesKey("primary_color")
|
||||
val PASSCODE = stringPreferencesKey("passcode")
|
||||
val IS_LOCK_APP = booleanPreferencesKey("is_lock_app")
|
||||
val DYNAMIC_COLOR = booleanPreferencesKey("dynamic_color")
|
||||
}
|
||||
|
||||
class DataStoreManager private constructor(private val context: Context) {
|
||||
companion object {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Volatile
|
||||
private var INSTANCE: DataStoreManager? = null
|
||||
|
||||
fun initialize(context: Context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized(this) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = DataStoreManager(context.applicationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstance(): DataStoreManager {
|
||||
return INSTANCE ?: throw IllegalStateException("DataStoreManager is not initialized")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> setValue(
|
||||
key: Preferences.Key<T>,
|
||||
value: T
|
||||
) {
|
||||
context.dataStore.edit { settings ->
|
||||
settings[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> getValue(
|
||||
key: Preferences.Key<T>,
|
||||
defaultValue: T
|
||||
): Flow<T> {
|
||||
return context.dataStore.data.map { pref ->
|
||||
pref[key] ?: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveToken(token: String) = setValue(
|
||||
Keys.TOKEN,
|
||||
token
|
||||
)
|
||||
|
||||
suspend fun savePasscode(passcode: String) = setValue(
|
||||
Keys.PASSCODE,
|
||||
passcode
|
||||
)
|
||||
|
||||
suspend fun saveIsLockApp(isLock: Boolean) = setValue(
|
||||
Keys.IS_LOCK_APP,
|
||||
isLock
|
||||
)
|
||||
|
||||
suspend fun savePrimaryColor(colorName: String) = setValue(
|
||||
Keys.PRIMARY_COLOR,
|
||||
colorName
|
||||
)
|
||||
|
||||
suspend fun saveTheme(theme: ThemeOption) = setValue(
|
||||
Keys.THEME,
|
||||
theme.toString()
|
||||
)
|
||||
|
||||
suspend fun saveDynamicColor(dynamicColor: Boolean) = setValue(
|
||||
Keys.DYNAMIC_COLOR,
|
||||
dynamicColor
|
||||
)
|
||||
|
||||
fun getToken() = getValue(
|
||||
Keys.TOKEN,
|
||||
""
|
||||
)
|
||||
|
||||
fun getPasscode() = getValue(
|
||||
Keys.PASSCODE,
|
||||
""
|
||||
)
|
||||
|
||||
fun getIsLockApp() = getValue(
|
||||
Keys.IS_LOCK_APP,
|
||||
false
|
||||
)
|
||||
|
||||
fun getPrimaryColor() = getValue(
|
||||
Keys.PRIMARY_COLOR,
|
||||
PrimaryColorOption.Blue.name
|
||||
)
|
||||
|
||||
fun getTheme() = getValue(
|
||||
Keys.THEME,
|
||||
ThemeOption.SYSTEM.name
|
||||
)
|
||||
|
||||
fun getDynamicColor() = getValue(
|
||||
Keys.DYNAMIC_COLOR,
|
||||
false
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import android.os.Build
|
||||
import javax.inject.Inject
|
||||
|
||||
class DeviceHelper @Inject constructor() {
|
||||
|
||||
fun getDeviceName(): String {
|
||||
val manufacturer = Build.MANUFACTURER
|
||||
val model = Build.MODEL
|
||||
|
||||
return if (model.startsWith(manufacturer, ignoreCase = true)) {
|
||||
model
|
||||
} else {
|
||||
"$manufacturer $model"
|
||||
}
|
||||
}
|
||||
|
||||
fun getOsVersion(): String = Build.VERSION.RELEASE
|
||||
|
||||
fun getOsName(): String = "Android"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
|
||||
class DialogController {
|
||||
|
||||
var isVisible by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
fun show() {
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.aiwazian.messenger.services
|
||||
|
||||
import com.aiwazian.messenger.api.RetrofitInstance
|
||||
import com.aiwazian.messenger.data.FolderInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
class FolderService @Inject constructor() {
|
||||
|
||||
suspend fun getAll(): List<FolderInfo>? {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
suspend fun save(folderInfo: FolderInfo): Int? {
|
||||
val request = RetrofitInstance.api.saveFolder(folderInfo)
|
||||
|
||||
val savedFolderId = request.body()?.message?.toInt()
|
||||
|
||||
return savedFolderId
|
||||
}
|
||||
|
||||
suspend fun remove(folderId: Int): Boolean {
|
||||
val request = RetrofitInstance.api.deleteFolder(folderId)
|
||||
|
||||
return request.isSuccessful
|
||||
}
|
||||
}
|
||||
Loaded 100 of 383 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user