diff --git a/.github/workflows/android_dev.yml b/.github/workflows/android_dev.yml
new file mode 100644
index 00000000..ac2cdedd
--- /dev/null
+++ b/.github/workflows/android_dev.yml
@@ -0,0 +1,48 @@
+name: Android CI
+
+on:
+ pull_request:
+ branches: [ "dev" ]
+ push:
+ branches: [ "dev" ]
+
+env:
+ KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+ RELEASE_SIGN_KEY_ALIAS: ${{ secrets.RELEASE_SIGN_KEY_ALIAS }}
+ RELEASE_SIGN_KEY_PASSWORD: ${{ secrets.RELEASE_SIGN_KEY_PASSWORD }}
+
+jobs:
+ build_apk_aab:
+ runs-on: ubuntu-latest
+ name: Build dev artifacts
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build and sign debug APKs
+ run: ./gradlew assembleDebug
+
+ - name: Build and sign release APKs
+ run: ./gradlew assembleRelease
+
+ - name: Upload dev-debug APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-dev-debug.apk
+ path: app/build/outputs/apk/amethyst/debug/app-amethyst-debug.apk
+
+ - name: Upload dev-release APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-dev-release.apk
+ path: app/build/outputs/apk/amethyst/release/app-amethyst-release.apk
diff --git a/.github/workflows/android_master.yml b/.github/workflows/android_master.yml
new file mode 100644
index 00000000..d32fee80
--- /dev/null
+++ b/.github/workflows/android_master.yml
@@ -0,0 +1,47 @@
+name: Android CI
+
+on:
+ push:
+ branches: [ "master" ]
+
+env:
+ KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+ RELEASE_SIGN_KEY_ALIAS: ${{ secrets.RELEASE_SIGN_KEY_ALIAS }}
+ RELEASE_SIGN_KEY_PASSWORD: ${{ secrets.RELEASE_SIGN_KEY_PASSWORD }}
+
+jobs:
+ build_apk_aab:
+ runs-on: ubuntu-latest
+ name: Build full artifacts
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build and sign debug APKs
+ run: ./gradlew assembleDebug
+
+ - name: Build and sign release APKs
+ run: ./gradlew assembleRelease
+
+ - name: Upload full-debug APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-full-debug.apk
+ path: app/build/outputs/apk/full/debug/app-full-debug.apk
+
+ - name: Upload full-release APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-full-release.apk
+ path: app/build/outputs/apk/full/release/app-full-release.apk
diff --git a/.gitignore b/.gitignore
index de251274..7ccead02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,9 +8,10 @@
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
-/build
+build/
/captures
.externalNativeBuild
.cxx
local.properties
-.idea
\ No newline at end of file
+.idea
+/.kotlin
diff --git a/README.md b/README.md
index b1bdbdf1..bc048887 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,82 @@
+### Module Graph
+
+```mermaid
+%%{
+ init: {
+ 'theme': 'base',
+ 'themeVariables': {"primaryTextColor":"#fff","primaryColor":"#5a4f7c","primaryBorderColor":"#5a4f7c","lineColor":"#f5a623","tertiaryColor":"#40375c","fontSize":"12px"}
+ }
+}%%
+
+graph LR
+ subgraph :core
+ :core:database["database"]
+ :core:model["model"]
+ :core:data["data"]
+ :core:ui["ui"]
+ :core:common["common"]
+ :core:designsystem["designsystem"]
+ :core:datastore["datastore"]
+ :core:network["network"]
+ end
+ subgraph :feature
+ :feature:chatmaterials["chatmaterials"]
+ :feature:messageshistory["messageshistory"]
+ :feature:settings["settings"]
+ :feature:languagepicker["languagepicker"]
+ :feature:userbanned["userbanned"]
+ :feature:auth["auth"]
+ :feature:conversations["conversations"]
+ :feature:photoviewer["photoviewer"]
+ end
+ :core:database --> :core:model
+ :feature:chatmaterials --> :core:data
+ :feature:chatmaterials --> :core:model
+ :feature:chatmaterials --> :core:ui
+ :feature:messageshistory --> :core:data
+ :feature:messageshistory --> :core:model
+ :feature:messageshistory --> :core:ui
+ :feature:settings --> :core:data
+ :feature:settings --> :core:model
+ :feature:settings --> :core:ui
+ :feature:languagepicker --> :core:data
+ :feature:languagepicker --> :core:model
+ :feature:languagepicker --> :core:ui
+ :feature:userbanned --> :core:data
+ :feature:userbanned --> :core:model
+ :feature:userbanned --> :core:ui
+ :app --> :feature:auth
+ :app --> :feature:chatmaterials
+ :app --> :feature:conversations
+ :app --> :feature:languagepicker
+ :app --> :feature:messageshistory
+ :app --> :feature:photoviewer
+ :app --> :feature:settings
+ :app --> :feature:userbanned
+ :app --> :core:common
+ :app --> :core:ui
+ :app --> :core:designsystem
+ :app --> :core:data
+ :app --> :core:model
+ :app --> :core:datastore
+ :core:data --> :core:common
+ :core:data --> :core:model
+ :core:data --> :core:network
+ :core:data --> :core:database
+ :core:network --> :core:common
+ :core:network --> :core:model
+ :feature:auth --> :core:data
+ :feature:auth --> :core:ui
+ :core:ui --> :core:designsystem
+ :core:ui --> :core:model
+ :feature:photoviewer --> :core:data
+ :feature:photoviewer --> :core:model
+ :feature:photoviewer --> :core:ui
+ :feature:conversations --> :core:data
+ :feature:conversations --> :core:model
+ :feature:conversations --> :core:ui
+ :core:datastore --> :core:common
+```
# fast-messenger
-Unofficial messenger for russian social network VKontakte
+Unofficial messenger for russian social network VKontakte
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
index 42afabfd..3b1ff2da 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1 +1,3 @@
-/build
\ No newline at end of file
+/build
+/keystore/keystore.properties
+/full
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 39128ba6..ce45158d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,226 +1,184 @@
-@file:Suppress("UnstableApiUsage")
-
-import com.android.build.gradle.internal.api.BaseVariantOutputImpl
-import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
-
-val sdkPackage: String = gradleLocalProperties(rootDir).getProperty("sdkPackage", "\"\"")
-val sdkFingerprint: String = gradleLocalProperties(rootDir).getProperty("sdkFingerprint", "\"\"")
-
-val msAppCenterToken: String =
- gradleLocalProperties(rootDir).getProperty("msAppCenterAppToken", "\"\"")
-val otaSecretCode: String = gradleLocalProperties(rootDir).getProperty("otaSecretCode", "\"\"")
-
-val majorVersion = 1
-val minorVersion = 6
-val patchVersion = 4
+import java.util.Properties
plugins {
- id("com.android.application")
- id("kotlin-android")
- id("kotlin-kapt")
- id("kotlin-parcelize")
- id("org.jetbrains.kotlin.android")
- id("com.google.devtools.ksp")
+ alias(libs.plugins.com.android.application)
+ alias(libs.plugins.org.jetbrains.kotlin.android)
+ alias(libs.plugins.org.jetbrains.kotlin.plugin.parcelize)
+ alias(libs.plugins.com.google.devtools.ksp)
+ alias(libs.plugins.kotlin.compose.compiler)
+ alias(libs.plugins.kotlin.serialization)
}
android {
- namespace = "com.meloda.fast"
-
- compileSdk = 34
-
- applicationVariants.all {
- outputs.all {
- (this as BaseVariantOutputImpl).outputFileName =
- "${name}-${versionName}-${versionCode}.apk"
- }
- }
+ namespace = "com.meloda.app.fast"
+ compileSdk = Configs.compileSdk
defaultConfig {
- applicationId = "com.meloda.fast"
- minSdk = 24
- targetSdk = 34
- versionCode = 1
- versionName = "alpha"
+ applicationId = "com.meloda.app.fast"
+ minSdk = Configs.minSdk
+ targetSdk = Configs.targetSdk
+ versionCode = Configs.appCode
+ versionName = Configs.appName
- javaCompileOptions {
- annotationProcessorOptions {
-// arguments += mapOf("room.schemaLocation" to "$projectDir/schemas")
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ // TODO: 06/05/2024, Danil Nikolaev: придумать, как совместить с github actions
+// applicationVariants.all {
+// val variant = this
+// variant.outputs
+// .map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
+// .forEach { output ->
+// if (variant.buildType.name == "release") {
+// val outputFileName = "fastvk-v${variant.versionName}-${variant.flavorName}.apk"
+// output.outputFileName = outputFileName
+// }
+// }
+// }
+
+ signingConfigs {
+ create("release") {
+ val keystoreProperties = Properties()
+ val keystorePropertiesFile = file("keystore/keystore.properties")
+
+ storeFile = file("keystore/keystore.jks")
+
+ if (keystorePropertiesFile.exists()) {
+ keystorePropertiesFile.inputStream().let(keystoreProperties::load)
+ storePassword = keystoreProperties.getProperty("storePassword")
+ keyAlias = keystoreProperties.getProperty("keyAlias")
+ keyPassword = keystoreProperties.getProperty("keyPassword")
+ } else {
+ storePassword = System.getenv("KEYSTORE_PASSWORD")
+ keyAlias = System.getenv("RELEASE_SIGN_KEY_ALIAS")
+ keyPassword = System.getenv("RELEASE_SIGN_KEY_PASSWORD")
}
}
+
+ create("debugSigning") {
+ initWith(getByName("release"))
+ }
}
buildTypes {
- getByName("debug") {
- buildConfigField("String", "sdkPackage", sdkPackage)
- buildConfigField("String", "sdkFingerprint", sdkFingerprint)
-
- buildConfigField("String", "msAppCenterAppToken", msAppCenterToken)
-
- buildConfigField("String", "otaSecretCode", otaSecretCode)
-
- versionNameSuffix = "_${getVersionName()}"
+ named("debug") {
+ signingConfig = signingConfigs.getByName("debugSigning")
+ applicationIdSuffix = ".debug"
}
- getByName("release") {
- isMinifyEnabled = false
+ named("release") {
+ signingConfig = signingConfigs.getByName("release")
- buildConfigField("String", "sdkPackage", sdkPackage)
- buildConfigField("String", "sdkFingerprint", sdkFingerprint)
-
- buildConfigField("String", "msAppCenterAppToken", msAppCenterToken)
-
- buildConfigField("String", "otaSecretCode", otaSecretCode)
+ isMinifyEnabled = true
+ isShrinkResources = true
proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
)
}
+
+ // TODO: 15/05/2024, Danil Nikolaev: add to other modules with build convention
+ register("staging") {
+ initWith(getByName("release"))
+ applicationIdSuffix = ".staging"
+ }
}
- val flavorDimension = "version"
-
+ val flavorDimension = "variant"
flavorDimensions += flavorDimension
productFlavors {
- create("dev") {
- resourceConfigurations += listOf("en", "xxhdpi")
-
- dimension = flavorDimension
- applicationIdSuffix = ".dev"
- versionNameSuffix = "-dev"
- }
- create("full") {
+ register("amethyst") {
dimension = flavorDimension
+ isDefault = true
}
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
+ sourceCompatibility = Configs.java
+ targetCompatibility = Configs.java
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_17.toString()
+ jvmTarget = Configs.java.toString()
freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn", "-Xcontext-receivers")
}
buildFeatures {
- viewBinding = true
compose = true
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.4.5"
useLiveLiterals = true
}
- packagingOptions {
- jniLibs {
- useLegacyPackaging = false
- }
- }
+
+// packaging {
+// resources {
+// excludes += "/META-INF/{AL2.0,LGPL2.1}"
+// }
+// }
}
-kapt {
- correctErrorTypes = true
-}
-
-fun getVersionName() = "$majorVersion.$minorVersion.$patchVersion"
-
-val currentTime get() = (System.currentTimeMillis() / 1000).toInt()
-
dependencies {
+ implementation(projects.feature.auth)
+ implementation(projects.feature.chatmaterials)
+ implementation(projects.feature.conversations)
+ implementation(projects.feature.languagepicker)
+ implementation(projects.feature.messageshistory)
+ implementation(projects.feature.photoviewer)
+ implementation(projects.feature.settings)
+ implementation(projects.feature.friends)
+ implementation(projects.feature.profile)
+ implementation(projects.core.common)
+ implementation(projects.core.ui)
+ implementation(projects.core.designsystem)
+ implementation(projects.core.data)
+ implementation(projects.core.model)
+ implementation(projects.core.datastore)
- // DI zone
- implementation("io.insert-koin:koin-android:3.4.0")
- // end of DI zone
+ // Tests zone
+ testImplementation(libs.junit)
+ // end of Tests zone
- implementation("com.github.skydoves:cloudy:0.1.2")
+ // Compose-Bom zone
+ implementation(platform(libs.compose.bom))
+ implementation(libs.bundles.compose)
+ // end of Compose-Bom zone
- implementation("io.coil-kt:coil-compose:2.3.0")
- implementation("io.coil-kt:coil:2.3.0")
+ implementation(libs.accompanist.permissions)
- implementation("com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2")
- implementation("com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2")
+ // Coil for Compose
+ implementation(libs.coil.compose)
- implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.21")
+ androidTestImplementation(libs.compose.ui.test.junit4)
+ debugImplementation(libs.compose.ui.test.manifest)
+ debugImplementation(libs.compose.ui.tooling)
- implementation("androidx.core:core-ktx:1.10.1")
+ implementation(libs.koin.android)
+ implementation(libs.koin.androidx.compose)
+ implementation(libs.koin.androidx.compose.navigation)
- implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
- implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
+ implementation(libs.coil)
- implementation("androidx.core:core-splashscreen:1.0.1")
+ implementation(libs.core.ktx)
- implementation("androidx.appcompat:appcompat:1.6.1")
+ implementation(libs.lifecycle.viewmodel.ktx)
+ implementation(libs.lifecycle.runtime.ktx)
- implementation("androidx.activity:activity-ktx:1.7.2")
+ implementation(libs.preference.ktx)
+ implementation(libs.material)
- implementation("androidx.fragment:fragment-ktx:1.6.1")
+ implementation(libs.haze)
+ implementation(libs.haze.materials)
- implementation("androidx.preference:preference-ktx:1.2.0")
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.kotlinx.coroutines.android)
- implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
+ implementation(libs.nanokt)
+ implementation(libs.nanokt.android)
+ implementation(libs.nanokt.jvm)
- implementation("androidx.recyclerview:recyclerview:1.3.1")
-
- implementation("androidx.constraintlayout:constraintlayout:2.1.4")
-
- implementation("com.google.accompanist:accompanist-systemuicontroller:0.27.0")
-
- implementation("androidx.room:room-ktx:2.5.2")
- implementation("androidx.room:room-runtime:2.5.2")
- ksp("androidx.room:room-compiler:2.5.2")
-
- implementation("com.github.terrakok:cicerone:7.1")
-
- implementation("com.github.massoudss:waveformSeekBar:5.0.0")
-
- implementation("com.github.bumptech.glide:glide:4.15.1")
- ksp("com.github.bumptech.glide:compiler:4.15.1")
-
- implementation("com.github.fondesa:kpermissions:3.4.0")
- implementation("com.github.fondesa:kpermissions-coroutines:3.4.0")
-
- implementation("com.microsoft.appcenter:appcenter-analytics:5.0.1")
- implementation("com.microsoft.appcenter:appcenter-crashes:5.0.1")
-
- implementation("com.squareup.retrofit2:retrofit:2.9.0")
- implementation("com.squareup.retrofit2:converter-gson:2.9.0")
-
- implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.11")
-
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.7.1")
-
- implementation("com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9")
-
- implementation("com.google.code.gson:gson:2.10.1")
-
- implementation("com.google.guava:guava:31.1-jre")
-
- implementation("com.google.android.material:material:1.9.0")
-
- implementation("com.github.chuckerteam.chucker:library:3.5.2")
-
- implementation("dev.chrisbanes.insetter:insetter:0.6.1")
-
- // Compose zone
- implementation(platform("androidx.compose:compose-bom:2023.04.01"))
-
- implementation("androidx.compose.material3:material3:1.1.1")
-// implementation("androidx.compose.material:material:1.4.3")
- implementation("androidx.compose.ui:ui:1.4.3")
-
- implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
- debugImplementation("androidx.compose.ui:ui-tooling:1.4.3")
-
- implementation("androidx.compose.material3:material3-window-size-class:1.1.1")
-
- implementation("androidx.activity:activity-compose:1.7.2")
- implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
- implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
-
- implementation("androidx.compose.runtime:runtime-saveable:1.6.0-alpha02")
- // end of Compose zone
+ implementation(libs.androidx.navigation.compose)
+ implementation(libs.kotlin.serialization)
}
diff --git a/app/keystore/keystore.jks b/app/keystore/keystore.jks
new file mode 100644
index 00000000..1d39b907
Binary files /dev/null and b/app/keystore/keystore.jks differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index ff59496d..2f9dc5a4 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -18,4 +18,4 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-renamesourcefileattribute SourceFile
diff --git a/app/schemas/com.meloda.fast.database.AccountsDatabase/2.json b/app/schemas/com.meloda.fast.database.AccountsDatabase/2.json
new file mode 100644
index 00000000..116a04f5
--- /dev/null
+++ b/app/schemas/com.meloda.fast.database.AccountsDatabase/2.json
@@ -0,0 +1,52 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "3ebd234270e36902d3d461af38664869",
+ "entities": [
+ {
+ "tableName": "accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `accessToken` TEXT NOT NULL, `fastToken` TEXT, `trustedHash` TEXT, PRIMARY KEY(`userId`))",
+ "fields": [
+ {
+ "fieldPath": "userId",
+ "columnName": "userId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accessToken",
+ "columnName": "accessToken",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fastToken",
+ "columnName": "fastToken",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "trustedHash",
+ "columnName": "trustedHash",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "userId"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3ebd234270e36902d3d461af38664869')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.meloda.fast.database.CacheDatabase/3.json b/app/schemas/com.meloda.fast.database.CacheDatabase/3.json
new file mode 100644
index 00000000..0e8d3d0f
--- /dev/null
+++ b/app/schemas/com.meloda.fast.database.CacheDatabase/3.json
@@ -0,0 +1,424 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 3,
+ "identityHash": "6a940f719e8dd56ea5c196c152f1e536",
+ "entities": [
+ {
+ "tableName": "users",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `isOnline` INTEGER NOT NULL, `isOnlineMobile` INTEGER NOT NULL, `onlineAppId` INTEGER, `lastSeen` INTEGER, `lastSeenStatus` TEXT, `birthday` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "firstName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastName",
+ "columnName": "lastName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isOnline",
+ "columnName": "isOnline",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isOnlineMobile",
+ "columnName": "isOnlineMobile",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "onlineAppId",
+ "columnName": "onlineAppId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastSeen",
+ "columnName": "lastSeen",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastSeenStatus",
+ "columnName": "lastSeenStatus",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "birthday",
+ "columnName": "birthday",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo50",
+ "columnName": "photo50",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo100",
+ "columnName": "photo100",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo200",
+ "columnName": "photo200",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "groups",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `screenName` TEXT NOT NULL, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `membersCount` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "screenName",
+ "columnName": "screenName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "photo50",
+ "columnName": "photo50",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo100",
+ "columnName": "photo100",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo200",
+ "columnName": "photo200",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "membersCount",
+ "columnName": "membersCount",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `text` TEXT, `isOut` INTEGER NOT NULL, `peerId` INTEGER NOT NULL, `fromId` INTEGER NOT NULL, `date` INTEGER NOT NULL, `randomId` INTEGER NOT NULL, `action` TEXT, `actionMemberId` INTEGER, `actionText` TEXT, `actionConversationMessageId` INTEGER, `actionMessage` TEXT, `updateTime` INTEGER, `important` INTEGER NOT NULL, `forwardIds` TEXT, `attachments` TEXT, `replyMessageId` INTEGER, `geoType` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isOut",
+ "columnName": "isOut",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "peerId",
+ "columnName": "peerId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fromId",
+ "columnName": "fromId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "randomId",
+ "columnName": "randomId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "action",
+ "columnName": "action",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "actionMemberId",
+ "columnName": "actionMemberId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "actionText",
+ "columnName": "actionText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "actionConversationMessageId",
+ "columnName": "actionConversationMessageId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "actionMessage",
+ "columnName": "actionMessage",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "updateTime",
+ "columnName": "updateTime",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "important",
+ "columnName": "important",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "forwardIds",
+ "columnName": "forwardIds",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "replyMessageId",
+ "columnName": "replyMessageId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "geoType",
+ "columnName": "geoType",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "conversations",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localId` INTEGER NOT NULL, `ownerId` INTEGER, `title` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `isPhantom` INTEGER NOT NULL, `lastConversationMessageId` INTEGER NOT NULL, `inReadCmId` INTEGER NOT NULL, `outReadCmId` INTEGER NOT NULL, `inRead` INTEGER NOT NULL, `outRead` INTEGER NOT NULL, `lastMessageId` INTEGER, `unreadCount` INTEGER NOT NULL, `membersCount` INTEGER, `canChangePin` INTEGER NOT NULL, `canChangeInfo` INTEGER NOT NULL, `majorId` INTEGER NOT NULL, `minorId` INTEGER NOT NULL, `pinnedMessageId` INTEGER, `peerType` TEXT NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localId",
+ "columnName": "localId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerId",
+ "columnName": "ownerId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo50",
+ "columnName": "photo50",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo100",
+ "columnName": "photo100",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "photo200",
+ "columnName": "photo200",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isPhantom",
+ "columnName": "isPhantom",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastConversationMessageId",
+ "columnName": "lastConversationMessageId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "inReadCmId",
+ "columnName": "inReadCmId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "outReadCmId",
+ "columnName": "outReadCmId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "inRead",
+ "columnName": "inRead",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "outRead",
+ "columnName": "outRead",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastMessageId",
+ "columnName": "lastMessageId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadCount",
+ "columnName": "unreadCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "membersCount",
+ "columnName": "membersCount",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "canChangePin",
+ "columnName": "canChangePin",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canChangeInfo",
+ "columnName": "canChangeInfo",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "majorId",
+ "columnName": "majorId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "minorId",
+ "columnName": "minorId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pinnedMessageId",
+ "columnName": "pinnedMessageId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "peerType",
+ "columnName": "peerType",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6a940f719e8dd56ea5c196c152f1e536')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/com/meloda/fast/tests/LoginSignInTest.kt b/app/src/androidTest/kotlin/com/meloda/fast/tests/LoginSignInTest.kt
new file mode 100644
index 00000000..f436216c
--- /dev/null
+++ b/app/src/androidTest/kotlin/com/meloda/fast/tests/LoginSignInTest.kt
@@ -0,0 +1,46 @@
+package com.meloda.app.fast.tests
+
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import org.junit.Rule
+import org.junit.Test
+
+class LoginSignInTest {
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun signInButtonIsClickable() {
+ composeTestRule.setContent {
+// LogoScreen(onAction = {})
+ }
+
+ composeTestRule.onNodeWithTag(testTag = "Sign in button").assertHasClickAction()
+ }
+
+ @Test
+ fun signInButtonTriggersSignInAction() {
+ var signInClicked = true
+
+ composeTestRule.setContent {
+// com.meloda.fast.auth.login.presentation.LogoScreen(
+// onAction = { action ->
+// when (action) {
+// UiAction.NextClicked -> {
+// signInClicked = true
+// }
+//
+// else -> Unit
+// }
+// }
+// )
+ }
+
+ composeTestRule.onNodeWithTag("Sign in button").performClick()
+
+ assert(signInClicked)
+ }
+}
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..5882fe4d
Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..ccde9097
Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..ff89976e
Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..25956942
Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..7fc94b56
Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/dev/res/values/strings.xml b/app/src/debug/res/values/ic_launcher_background.xml
similarity index 52%
rename from app/src/dev/res/values/strings.xml
rename to app/src/debug/res/values/ic_launcher_background.xml
index ede9730a..f2d61e27 100644
--- a/app/src/dev/res/values/strings.xml
+++ b/app/src/debug/res/values/ic_launcher_background.xml
@@ -1,6 +1,4 @@
-
- Fast Dev
-
+ #F44336
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
new file mode 100644
index 00000000..40a4e868
--- /dev/null
+++ b/app/src/debug/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Fast Debug
+
diff --git a/app/src/dev/res/mipmap-hdpi/ic_launcher.png b/app/src/dev/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 037e657b..00000000
Binary files a/app/src/dev/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/dev/res/mipmap-mdpi/ic_launcher.png b/app/src/dev/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index e0ee3795..00000000
Binary files a/app/src/dev/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/dev/res/mipmap-xhdpi/ic_launcher.png b/app/src/dev/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index a14bfbf5..00000000
Binary files a/app/src/dev/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png b/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8b839a55..00000000
Binary files a/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index c3b67b16..00000000
Binary files a/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1d0023bd..2cf96e8f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
@@ -9,29 +8,22 @@
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
-
-
@@ -39,10 +31,8 @@
-
-
@@ -53,33 +43,6 @@
android:exported="false"
android:foregroundServiceType="dataSync" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
= Build.VERSION_CODES.TIRAMISU &&
+ SettingsController.getBoolean(
+ SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
+ SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND
+ ))
+ }
+ }
+
+ if (isNeedToCheckNotificationsPermission) {
+ CheckPermission(
+ showRationale = {
+ MaterialDialog(
+ title = UiText.Resource(UiR.string.warning),
+ text = UiText.Simple("The application will not be able to work properly without permission to send notifications."),
+ confirmText = UiText.Simple("Grant"),
+ confirmAction = {
+ viewModel.onRequestNotificationsPermissionClicked(true)
+ },
+ cancelText = UiText.Resource(UiR.string.cancel),
+ cancelAction = viewModel::onNotificationsAlertNegativeClicked,
+ onDismissAction = viewModel::onNotificationsAlertNegativeClicked,
+ buttonsInvokeDismiss = false
+ )
+ },
+ onDenied = {
+ MaterialDialog(
+ title = UiText.Resource(UiR.string.warning),
+ text = UiText.Simple("The application needs permission to send notifications to update messages and other information."),
+ confirmText = UiText.Simple("Grant"),
+ confirmAction = {
+ viewModel.onRequestNotificationsPermissionClicked(false)
+ },
+ cancelText = UiText.Resource(UiR.string.cancel),
+ onDismissAction = {},
+ buttonsInvokeDismiss = false
+ )
+ },
+ permission = permission
+ )
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/MainGraph.kt b/app/src/main/kotlin/com/meloda/app/fast/MainGraph.kt
new file mode 100644
index 00000000..8253d8b1
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/MainGraph.kt
@@ -0,0 +1,196 @@
+package com.meloda.app.fast
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideIn
+import androidx.compose.animation.slideOut
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Settings
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.IntOffset
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navigation
+import androidx.navigation.compose.rememberNavController
+import com.meloda.app.fast.conversations.navigation.Conversations
+import com.meloda.app.fast.conversations.navigation.conversationsRoute
+import com.meloda.app.fast.friends.navigation.Friends
+import com.meloda.app.fast.friends.navigation.friendsRoute
+import com.meloda.app.fast.model.BaseError
+import kotlinx.serialization.Serializable
+import com.meloda.app.fast.designsystem.R as UiR
+
+@Serializable
+object MainGraph
+
+@Serializable
+object Main
+
+@Serializable
+object Profile
+
+data class BottomNavigationItem(
+ val titleResId: Int,
+ val selectedIconResId: Int,
+ val unselectedIconResId: Int,
+ val route: Any,
+)
+
+@OptIn(ExperimentalMaterial3Api::class)
+fun NavGraphBuilder.mainScreen(
+ onError: (BaseError) -> Unit,
+ onNavigateToSettings: () -> Unit,
+ onNavigateToMessagesHistory: (conversationId: Int) -> Unit,
+) {
+ val items = listOf(
+ BottomNavigationItem(
+ titleResId = UiR.string.title_friends,
+ selectedIconResId = UiR.drawable.baseline_people_alt_24,
+ unselectedIconResId = UiR.drawable.outline_people_alt_24,
+ route = Friends,
+ ),
+ BottomNavigationItem(
+ titleResId = UiR.string.title_conversations,
+ selectedIconResId = UiR.drawable.baseline_chat_24,
+ unselectedIconResId = UiR.drawable.outline_chat_24,
+ route = Conversations
+ ),
+ BottomNavigationItem(
+ titleResId = UiR.string.title_profile,
+ selectedIconResId = UiR.drawable.baseline_account_circle_24,
+ unselectedIconResId = UiR.drawable.outline_account_circle_24,
+ route = Profile
+ )
+ )
+ val routes = items.map(BottomNavigationItem::route)
+
+ composable {
+ val navController = rememberNavController()
+
+ var selectedItemIndex by rememberSaveable {
+ mutableIntStateOf(1)
+ }
+
+ var isBottomBarVisible by rememberSaveable {
+ mutableStateOf(true)
+ }
+
+ Scaffold(
+ bottomBar = {
+ AnimatedVisibility(
+ visible = isBottomBarVisible,
+ enter = slideIn { IntOffset(0, 400) },
+ exit = slideOut { IntOffset(0, 400) }
+ ) {
+ NavigationBar {
+ items.forEachIndexed { index, item ->
+ NavigationBarItem(
+ selected = selectedItemIndex == index,
+ onClick = {
+ if (selectedItemIndex != index) {
+ val currentRoute = routes[selectedItemIndex]
+
+ selectedItemIndex = index
+ navController.navigate(item.route) {
+ popUpTo(route = currentRoute) {
+ inclusive = true
+ }
+ }
+ }
+ },
+ icon = {
+ Icon(
+ painter = painterResource(
+ id = if (selectedItemIndex == index) item.selectedIconResId
+ else item.unselectedIconResId
+ ),
+ contentDescription = null
+ )
+ },
+ alwaysShowLabel = false
+ )
+ }
+ }
+ }
+ }
+ ) { padding ->
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(bottom = padding.calculateBottomPadding())
+ ) {
+ NavHost(
+ navController = navController,
+ startDestination = MainGraph,
+ enterTransition = { fadeIn(animationSpec = tween(200)) },
+ exitTransition = { fadeOut(animationSpec = tween(200)) }
+ ) {
+ navigation(startDestination = Conversations) {
+ friendsRoute(
+ onError = onError,
+ navController = navController
+ )
+ conversationsRoute(
+ onError = onError,
+ onNavigateToMessagesHistory = onNavigateToMessagesHistory,
+ navController = navController,
+ onListScrollingUp = { isScrolling ->
+// isBottomBarVisible = isScrolling
+ }
+ )
+
+ composable {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = {
+ Text(text = stringResource(id = UiR.string.title_profile))
+ },
+ actions = {
+ IconButton(onClick = onNavigateToSettings) {
+ Icon(
+ imageVector = Icons.Rounded.Settings,
+ contentDescription = null
+ )
+ }
+ }
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(padding)
+ ) {
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt b/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt
new file mode 100644
index 00000000..5d8b82c3
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt
@@ -0,0 +1,147 @@
+package com.meloda.app.fast
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.meloda.app.fast.common.UserConfig
+import com.meloda.app.fast.common.extensions.setValue
+import com.meloda.app.fast.common.extensions.updateValue
+import com.meloda.app.fast.data.db.AccountsRepository
+import com.meloda.app.fast.datastore.SettingsController
+import com.meloda.app.fast.datastore.SettingsKeys
+import com.meloda.app.fast.datastore.UserSettings
+import com.meloda.app.fast.model.BaseError
+import com.meloda.app.fast.model.LongPollState
+import com.meloda.app.fast.model.MainScreenState
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+interface MainViewModel {
+
+ val screenState: StateFlow
+
+ val longPollState: StateFlow
+ val startOnlineService: StateFlow
+
+ fun useDynamicColorsChanged(use: Boolean)
+
+ fun useDarkThemeChanged(use: Boolean)
+
+ fun onRequestNotificationsPermissionClicked(fromRationale: Boolean)
+ fun onNotificationsAlertNegativeClicked()
+
+ fun onNotificationsRequested()
+
+ fun onAppPermissionsOpened()
+
+ fun onError(error: BaseError)
+
+ fun onAuthOpened()
+}
+
+class MainViewModelImpl(
+ private val accountsRepository: AccountsRepository,
+ private val userSettings: UserSettings
+) : MainViewModel, ViewModel() {
+
+ init {
+ loadAccounts()
+ }
+
+ override val screenState = MutableStateFlow(MainScreenState.EMPTY)
+
+ override val longPollState = MutableStateFlow(
+ if (SettingsController.getBoolean(
+ SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
+ SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND
+ )
+ ) {
+ LongPollState.ForegroundService
+ } else {
+ LongPollState.DefaultService
+ }
+ )
+ override val startOnlineService = MutableStateFlow(
+ SettingsController.getBoolean(
+ SettingsKeys.KEY_VISIBILITY_SEND_ONLINE_STATUS,
+ SettingsKeys.DEFAULT_VALUE_KEY_VISIBILITY_SEND_ONLINE_STATUS
+ )
+ )
+
+ override fun useDynamicColorsChanged(use: Boolean) {
+ screenState.updateValue(screenState.value.copy(useDynamicColors = use))
+ }
+
+ override fun useDarkThemeChanged(use: Boolean) {
+ screenState.updateValue(screenState.value.copy(useDarkTheme = use))
+ }
+
+ override fun onRequestNotificationsPermissionClicked(fromRationale: Boolean) {
+ screenState.setValue { old ->
+ if (fromRationale) {
+ old.copy(isNeedToOpenAppPermissions = true)
+ } else {
+ old.copy(isNeedToRequestNotifications = true)
+ }
+ }
+ }
+
+ override fun onNotificationsAlertNegativeClicked() {
+ SettingsController.edit {
+ putBoolean(
+ SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
+ false
+ )
+ }
+ userSettings.setLongPollBackground(false)
+ }
+
+ override fun onNotificationsRequested() {
+ screenState.setValue { old -> old.copy(isNeedToRequestNotifications = false) }
+ }
+
+ override fun onAppPermissionsOpened() {
+ screenState.setValue { old -> old.copy(isNeedToOpenAppPermissions = false) }
+ }
+
+ override fun onError(error: BaseError) {
+ when (error) {
+ BaseError.SessionExpired -> {
+ screenState.setValue { old -> old.copy(isNeedToOpenAuth = true) }
+ }
+ }
+ }
+
+ override fun onAuthOpened() {
+ screenState.setValue { old -> old.copy(isNeedToOpenAuth = false) }
+ }
+
+ private fun loadAccounts() {
+ viewModelScope.launch(Dispatchers.IO) {
+ val accounts = accountsRepository.getAccounts()
+
+ Log.d("MainViewModel", "initUserConfig: accounts: $accounts")
+
+ if (accounts.isNotEmpty()) {
+ val currentAccount = accounts.find { it.userId == UserConfig.currentUserId }
+ if (currentAccount != null) {
+ UserConfig.apply {
+ this.userId = currentAccount.userId
+ this.accessToken = currentAccount.accessToken
+ this.fastToken = currentAccount.fastToken
+ this.trustedHash = currentAccount.trustedHash
+ }
+ }
+ }
+
+ screenState.setValue { old ->
+ old.copy(
+ accounts = accounts,
+ accountsLoaded = true
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/RootGraph.kt b/app/src/main/kotlin/com/meloda/app/fast/RootGraph.kt
new file mode 100644
index 00000000..806ae91b
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/RootGraph.kt
@@ -0,0 +1,92 @@
+package com.meloda.app.fast
+
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.rememberNavController
+import com.meloda.app.fast.auth.AuthGraph
+import com.meloda.app.fast.auth.authNavGraph
+import com.meloda.app.fast.auth.navigateToAuth
+import com.meloda.app.fast.chatmaterials.navigation.chatMaterialsRoute
+import com.meloda.app.fast.chatmaterials.navigation.navigateToChatMaterials
+import com.meloda.app.fast.common.UserConfig
+import com.meloda.app.fast.languagepicker.navigation.languagePickerRoute
+import com.meloda.app.fast.languagepicker.navigation.navigateToLanguagePicker
+import com.meloda.app.fast.messageshistory.navigation.messagesHistoryRoute
+import com.meloda.app.fast.messageshistory.navigation.navigateToMessagesHistory
+import com.meloda.app.fast.settings.presentation.navigateToSettings
+import com.meloda.app.fast.settings.presentation.settingsRoute
+import org.koin.androidx.compose.koinViewModel
+
+@Composable
+fun RootGraph(navController: NavHostController = rememberNavController()) {
+ val viewModel: MainViewModel = koinViewModel()
+ val screenState by viewModel.screenState.collectAsStateWithLifecycle()
+
+ if (screenState.isNeedToOpenAuth) {
+ viewModel.onAuthOpened()
+ navController.navigateToAuth(clearBackStack = true)
+ }
+
+ if (screenState.accountsLoaded) {
+ val isNeedToShowConversations by remember {
+ derivedStateOf { screenState.accounts.isNotEmpty() && UserConfig.isLoggedIn() }
+ }
+
+ NavHost(
+ navController = navController,
+ startDestination = if (isNeedToShowConversations) Main else AuthGraph,
+ enterTransition = { fadeIn(animationSpec = tween(200)) },
+ exitTransition = { fadeOut(animationSpec = tween(200)) }
+ ) {
+ authNavGraph(
+ onError = viewModel::onError,
+ onNavigateToMain = navController::navigateToMain,
+ navController = navController
+ )
+ mainScreen(
+ onError = viewModel::onError,
+ onNavigateToSettings = navController::navigateToSettings,
+ onNavigateToMessagesHistory = navController::navigateToMessagesHistory
+ )
+
+ messagesHistoryRoute(
+ onError = viewModel::onError,
+ onBack = navController::navigateUp,
+ onNavigateToChatAttachments = navController::navigateToChatMaterials
+ )
+ chatMaterialsRoute(
+ onBack = navController::navigateUp
+ )
+
+ settingsRoute(
+ onError = viewModel::onError,
+ onBack = navController::navigateUp,
+ onNavigateToAuth = { navController.navigateToAuth(true) },
+ onNavigateToLanguagePicker = navController::navigateToLanguagePicker
+ )
+ languagePickerRoute(onBack = navController::navigateUp)
+ }
+ }
+
+ NotificationsPermissionChecker(
+ screenState = screenState,
+ viewModel = viewModel
+ )
+}
+
+fun NavController.navigateToMain() {
+ this.navigate(Main) {
+ popUpTo(0) {
+ inclusive = true
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt
new file mode 100644
index 00000000..f76e8f06
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt
@@ -0,0 +1,35 @@
+package com.meloda.app.fast.common
+
+import android.app.Application
+import androidx.preference.PreferenceManager
+import coil.ImageLoader
+import coil.ImageLoaderFactory
+import com.meloda.app.fast.common.di.applicationModule
+import com.meloda.app.fast.datastore.SettingsController
+import org.koin.android.ext.android.get
+import org.koin.android.ext.koin.androidContext
+import org.koin.android.ext.koin.androidLogger
+import org.koin.core.context.GlobalContext.startKoin
+
+class AppGlobal : Application(), ImageLoaderFactory {
+
+ override fun onCreate() {
+ super.onCreate()
+
+ val preferences = PreferenceManager.getDefaultSharedPreferences(this)
+ SettingsController.init(preferences)
+ UserConfig.init(preferences)
+
+ initKoin()
+ }
+
+ private fun initKoin() {
+ startKoin {
+ androidLogger()
+ androidContext(this@AppGlobal)
+ modules(applicationModule)
+ }
+ }
+
+ override fun newImageLoader(): ImageLoader = get()
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/common/di/ApplicationModule.kt b/app/src/main/kotlin/com/meloda/app/fast/common/di/ApplicationModule.kt
new file mode 100644
index 00000000..411accb4
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/common/di/ApplicationModule.kt
@@ -0,0 +1,47 @@
+package com.meloda.app.fast.common.di
+
+import android.content.Context
+import android.content.res.Resources
+import android.os.PowerManager
+import androidx.preference.PreferenceManager
+import com.meloda.app.fast.MainViewModelImpl
+import com.meloda.app.fast.auth.authModule
+import com.meloda.app.fast.conversations.di.conversationsModule
+import com.meloda.app.fast.data.di.dataModule
+import com.meloda.app.fast.friends.di.friendsModule
+import com.meloda.app.fast.languagepicker.di.languagePickerModule
+import com.meloda.app.fast.messageshistory.di.messagesHistoryModule
+import com.meloda.app.fast.photoviewer.di.photoViewModule
+import com.meloda.app.fast.profile.di.profileModule
+import com.meloda.app.fast.service.longpolling.di.longPollModule
+import com.meloda.app.fast.settings.di.settingsModule
+import org.koin.android.ext.koin.androidContext
+import org.koin.androidx.viewmodel.dsl.viewModelOf
+import org.koin.core.module.dsl.singleOf
+import org.koin.core.qualifier.qualifier
+import org.koin.dsl.module
+
+val applicationModule = module {
+ includes(dataModule)
+ includes(
+ authModule,
+ conversationsModule,
+ settingsModule,
+ messagesHistoryModule,
+ photoViewModule,
+ languagePickerModule,
+ longPollModule,
+ friendsModule,
+ profileModule
+ )
+
+ // TODO: 14/05/2024, Danil Nikolaev: research on memory leaks and potentials errors
+ // TODO: 14/05/2024, Danil Nikolaev: extract all operations with preferences to standalone class
+ singleOf(PreferenceManager::getDefaultSharedPreferences)
+ single { androidContext().resources }
+ factory { androidContext().getSystemService(Context.POWER_SERVICE) as PowerManager }
+
+ viewModelOf(::MainViewModelImpl) {
+ qualifier = qualifier("main")
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/model/LongPollState.kt b/app/src/main/kotlin/com/meloda/app/fast/model/LongPollState.kt
new file mode 100644
index 00000000..6ef44939
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/model/LongPollState.kt
@@ -0,0 +1,7 @@
+package com.meloda.app.fast.model
+
+sealed class LongPollState {
+ data object ForegroundService : LongPollState()
+ data object DefaultService : LongPollState()
+ data object Stop : LongPollState()
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/model/MainScreenState.kt b/app/src/main/kotlin/com/meloda/app/fast/model/MainScreenState.kt
new file mode 100644
index 00000000..302a100d
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/model/MainScreenState.kt
@@ -0,0 +1,30 @@
+package com.meloda.app.fast.model
+
+import androidx.compose.runtime.Immutable
+import com.meloda.app.fast.model.database.AccountEntity
+
+@Immutable
+data class MainScreenState(
+ val accounts: List,
+ val accountsLoaded: Boolean,
+ val useDarkTheme: Boolean,
+ val useDynamicColors: Boolean,
+ val isNeedToRequestNotifications: Boolean,
+ val isNeedToOpenAppPermissions: Boolean,
+ val isNeedToOpenAuth: Boolean,
+) {
+
+ companion object {
+ val EMPTY: MainScreenState = MainScreenState(
+ accounts = emptyList(),
+ accountsLoaded = false,
+
+ // TODO: 05/05/2024, Danil Nikolaev: implement
+ useDarkTheme = false,
+ useDynamicColors = false,
+ isNeedToRequestNotifications = false,
+ isNeedToOpenAppPermissions = false,
+ isNeedToOpenAuth = false,
+ )
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/model/ServicesState.kt b/app/src/main/kotlin/com/meloda/app/fast/model/ServicesState.kt
new file mode 100644
index 00000000..664bf563
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/model/ServicesState.kt
@@ -0,0 +1,7 @@
+package com.meloda.app.fast.model
+
+sealed class ServicesState {
+ data object Started : ServicesState()
+ data object Stopped : ServicesState()
+ data object Unknown : ServicesState()
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/receiver/DownloadManagerReceiver.kt b/app/src/main/kotlin/com/meloda/app/fast/receiver/DownloadManagerReceiver.kt
similarity index 89%
rename from app/src/main/kotlin/com/meloda/fast/receiver/DownloadManagerReceiver.kt
rename to app/src/main/kotlin/com/meloda/app/fast/receiver/DownloadManagerReceiver.kt
index 5a80ffda..7b514d11 100644
--- a/app/src/main/kotlin/com/meloda/fast/receiver/DownloadManagerReceiver.kt
+++ b/app/src/main/kotlin/com/meloda/app/fast/receiver/DownloadManagerReceiver.kt
@@ -1,4 +1,4 @@
-package com.meloda.fast.receiver
+package com.meloda.app.fast.receiver
import android.content.BroadcastReceiver
import android.content.Context
@@ -12,4 +12,4 @@ class DownloadManagerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
onReceiveAction?.invoke()
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/service/OnlineService.kt b/app/src/main/kotlin/com/meloda/app/fast/service/OnlineService.kt
new file mode 100644
index 00000000..120e03ff
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/service/OnlineService.kt
@@ -0,0 +1,129 @@
+package com.meloda.app.fast.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import com.meloda.app.fast.common.UserConfig
+import com.meloda.app.fast.common.extensions.createTimerFlow
+import com.meloda.app.fast.data.api.account.AccountUseCase
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.launch
+import org.koin.android.ext.android.inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.time.Duration.Companion.minutes
+
+class OnlineService : Service() {
+
+ private val job = SupervisorJob()
+
+ private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
+ Log.d(TAG, "error: $throwable")
+ throwable.printStackTrace()
+ }
+
+ private val coroutineContext: CoroutineContext
+ get() = Dispatchers.Default + job + exceptionHandler
+
+ private val coroutineScope = CoroutineScope(coroutineContext)
+
+ private val useCase: AccountUseCase by inject()
+
+ private var timerJob: Job? = null
+ private var onlineJob: Job? = null
+
+ override fun onBind(intent: Intent?): IBinder? {
+ Log.d(STATE_TAG, "onBind: intent: $intent")
+ return null
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ if (startId > 1) return START_STICKY
+
+ Log.d(STATE_TAG, "onStartCommand: flags: $flags; startId: $startId\ninstance: $this")
+
+ // TODO: 05/05/2024, Danil Nikolaev: implement
+// if (AppGlobal.preferences.getBoolean(
+// SettingsKeys.KEY_VISIBILITY_SEND_ONLINE_STATUS,
+// SettingsKeys.DEFAULT_VALUE_KEY_VISIBILITY_SEND_ONLINE_STATUS
+// )
+// ) {
+// createTimer()
+// }
+
+ return START_STICKY
+ }
+
+ private fun createTimer() {
+ timerJob = createTimerFlow(
+ isNeedToEndCondition = { false },
+ onStartAction = ::setOnline,
+ onTickAction = ::setOnline,
+ interval = 5.minutes
+ ).launchIn(coroutineScope)
+ }
+
+ private fun setOnline() {
+ if (onlineJob != null) return
+
+
+ // TODO: 05/05/2024, Danil Nikolaev: implement
+// if (!AppGlobal.preferences.getBoolean(
+// SettingsKeys.KEY_VISIBILITY_SEND_ONLINE_STATUS,
+// SettingsKeys.DEFAULT_VALUE_KEY_VISIBILITY_SEND_ONLINE_STATUS
+// )
+// ) return
+
+ Log.d(TAG, "setOnline()")
+
+ onlineJob = coroutineScope.launch {
+ val token = UserConfig.fastToken ?: UserConfig.accessToken
+
+ if (token.isBlank()) {
+ Log.d(TAG, "setOnline: token is empty")
+ return@launch
+ }
+
+ val response = useCase.setOnline(
+ voip = false,
+ accessToken = token
+ )
+ Log.d(TAG, "setOnline: response: $response")
+ }.also { coroutine -> coroutine.invokeOnCompletion { onlineJob = null } }
+ }
+
+ private suspend fun setOffline() {
+ Log.d(TAG, "setOffline()")
+
+ val response = useCase.setOffline(
+ accessToken = UserConfig.accessToken
+ )
+
+ Log.d(TAG, "setOffline: response: $response")
+ }
+
+ override fun onLowMemory() {
+ Log.d(STATE_TAG, "onLowMemory")
+ super.onLowMemory()
+ }
+
+ override fun onDestroy() {
+ Log.d(STATE_TAG, "onDestroy")
+
+ timerJob?.cancel("OnlineService destroyed")
+ onlineJob?.cancel("OnlineService destroyed")
+
+ super.onDestroy()
+ }
+
+ companion object {
+ private const val TAG = "OnlineService"
+ private const val STATE_TAG = "OnlineServiceState"
+ }
+}
diff --git a/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt
new file mode 100644
index 00000000..3cb12fb2
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt
@@ -0,0 +1,271 @@
+package com.meloda.app.fast.service.longpolling
+
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Intent
+import android.content.SharedPreferences
+import android.net.Uri
+import android.os.Build
+import android.os.IBinder
+import android.provider.Settings
+import android.util.Log
+import androidx.core.app.NotificationCompat
+import androidx.core.app.ServiceCompat
+import com.conena.nanokt.android.app.stopForegroundCompat
+import com.meloda.app.fast.common.UserConfig
+import com.meloda.app.fast.common.VkConstants
+import com.meloda.app.fast.common.extensions.listenValue
+import com.meloda.app.fast.data.LongPollUpdatesParser
+import com.meloda.app.fast.data.LongPollUseCase
+import com.meloda.app.fast.data.processState
+import com.meloda.app.fast.datastore.SettingsController
+import com.meloda.app.fast.datastore.SettingsKeys
+import com.meloda.app.fast.model.api.data.LongPollUpdates
+import com.meloda.app.fast.model.api.data.VkLongPollData
+import com.meloda.app.fast.util.NotificationsUtils
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+import org.koin.android.ext.android.inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+class LongPollingService : Service() {
+
+ private val job = SupervisorJob()
+
+ private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
+ Log.e(TAG, "error: $throwable")
+
+ if (throwable !is NoAccessTokenException) {
+ throwable.printStackTrace()
+ }
+ }
+
+ private val coroutineContext: CoroutineContext
+ get() = Dispatchers.IO + job + exceptionHandler
+
+ private val coroutineScope = CoroutineScope(coroutineContext)
+
+ private val longPollUseCase: LongPollUseCase by inject()
+ private val updatesParser: LongPollUpdatesParser by inject()
+ private val preferences: SharedPreferences by inject()
+
+ private var currentJob: Job? = null
+
+ override fun onCreate() {
+ super.onCreate()
+ Log.d(STATE_TAG, "onCreate()")
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ Log.d(STATE_TAG, "onBind: intent: $intent")
+ return null
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ if (startId > 1) return START_STICKY
+
+ val asForeground = preferences.getBoolean(
+ SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
+ SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND
+ )
+
+ Log.d(
+ STATE_TAG,
+ "onStartCommand: asForeground: $asForeground; flags: $flags; startId: $startId;\ninstance: $this"
+ )
+
+ if (currentJob != null) {
+ currentJob?.cancel()
+ currentJob = null
+ }
+
+ coroutineScope.launch {
+ currentJob = startPolling().also { it.join() }
+ }
+
+ val openCategorySettingsIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
+ .putExtra(Settings.EXTRA_CHANNEL_ID, "long_polling")
+ } else {
+ Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", packageName, null))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ val openCategorySettingsPendingIntent = PendingIntent.getActivity(
+ this,
+ 1,
+ openCategorySettingsIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ if (asForeground) {
+ val notification =
+ NotificationsUtils.createNotification(
+ context = this,
+ title = "LongPoll",
+ contentText = "нажмите, чтобы убрать уведомление",
+ notRemovable = false,
+ channelId = "long_polling",
+ priority = NotificationsUtils.NotificationPriority.Low,
+ category = NotificationCompat.CATEGORY_SERVICE,
+ customNotificationId = NOTIFICATION_ID,
+ contentIntent = openCategorySettingsPendingIntent
+ ).build()
+
+ startForeground(NOTIFICATION_ID, notification)
+ } else {
+ stopForegroundCompat(ServiceCompat.STOP_FOREGROUND_REMOVE)
+ }
+ return START_STICKY
+ }
+
+ private fun startPolling(): Job {
+ if (job.isCompleted || job.isCancelled) {
+ Log.d(STATE_TAG, "job is completed or cancelled")
+ throw Exception("Job is over")
+ }
+
+ Log.d(STATE_TAG, "job started")
+
+ return coroutineScope.launch {
+ if (UserConfig.accessToken.isEmpty()) {
+ throw NoAccessTokenException
+ }
+
+ var serverInfo = getServerInfo()
+ ?: throw LongPollException(message = "bad VK response (server info)")
+
+ var lastUpdatesResponse: LongPollUpdates? = getUpdatesResponse(serverInfo)
+ ?: throw LongPollException(message = "initiation error: bad VK response (last updates)")
+
+ var failCount = 0
+
+ while (job.isActive) {
+ if (lastUpdatesResponse == null) {
+ failCount++
+ serverInfo = getServerInfo()
+ ?: throw LongPollException(message = "failed retrieving server info after error: bad VK response (server info #2)")
+ lastUpdatesResponse = getUpdatesResponse(serverInfo)
+ continue
+ }
+
+ when (lastUpdatesResponse.failed) {
+ 1 -> {
+ val newTs = lastUpdatesResponse.ts ?: kotlin.run {
+ failCount++
+ serverInfo.ts
+ }
+
+ lastUpdatesResponse = getUpdatesResponse(serverInfo.copy(ts = newTs))
+ }
+
+ 2, 3 -> {
+ serverInfo = getServerInfo()
+ ?: throw LongPollException(
+ message = "failed retrieving server info after error: bad VK response (server info #3)"
+ )
+ lastUpdatesResponse = getUpdatesResponse(serverInfo)
+ }
+
+ else -> {
+ val newTs = lastUpdatesResponse.ts
+
+ if (newTs == null) {
+ failCount++
+ } else {
+ val updates = lastUpdatesResponse.updates
+
+ if (updates == null) {
+ failCount++
+ } else {
+ updates.forEach(updatesParser::parseNextUpdate)
+ }
+
+ lastUpdatesResponse = getUpdatesResponse(serverInfo.copy(ts = newTs))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun getServerInfo(): VkLongPollData? = suspendCoroutine {
+ longPollUseCase.getLongPollServer(
+ needPts = true,
+ version = VkConstants.LP_VERSION
+ ).listenValue(coroutineScope) { state ->
+ state.processState(
+ success = { response ->
+ Log.d(TAG, "getServerInfo: serverInfoResponse: $response")
+ it.resume(response)
+ },
+ error = { error ->
+ Log.e(TAG, "getServerInfo: $error")
+ it.resume(null)
+ }
+ )
+ }
+ }
+
+ private suspend fun getUpdatesResponse(
+ server: VkLongPollData
+ ): LongPollUpdates? = suspendCoroutine {
+ longPollUseCase.getLongPollUpdates(
+ serverUrl = "https://${server.server}",
+ key = server.key,
+ ts = server.ts,
+ wait = 25,
+ mode = 2 or 8 or 32 or 64 or 128,
+ version = VkConstants.LP_VERSION
+ ).listenValue(coroutineScope) { state ->
+ state.processState(
+ success = { response ->
+ Log.d(TAG, "lastUpdateResponse: $response")
+ it.resume(response)
+ },
+ error = { error ->
+ Log.d(TAG, "getUpdatesResponse: error: $error")
+ it.resume(null)
+ }
+ )
+ }
+ }
+
+ override fun onDestroy() {
+ Log.d(STATE_TAG, "onDestroy")
+ try {
+ SettingsController.edit {
+ putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true)
+ }
+ job.cancel()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ super.onDestroy()
+ }
+
+ override fun onLowMemory() {
+ Log.d(STATE_TAG, "onLowMemory")
+ super.onLowMemory()
+ }
+
+ companion object {
+ const val TAG = "LongPollTask"
+
+ private const val STATE_TAG = "LongPollServiceState"
+
+ const val KEY_LONG_POLL_WAS_DESTROYED = "long_poll_was_destroyed"
+
+ private const val NOTIFICATION_ID = 1001
+ }
+}
+
+private data class LongPollException(override val message: String) : Throwable()
+private data object NoAccessTokenException : Throwable()
diff --git a/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/di/LongPollModule.kt b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/di/LongPollModule.kt
new file mode 100644
index 00000000..68a42c2f
--- /dev/null
+++ b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/di/LongPollModule.kt
@@ -0,0 +1,13 @@
+package com.meloda.app.fast.service.longpolling.di
+
+import com.meloda.app.fast.data.LongPollUpdatesParser
+import com.meloda.app.fast.data.LongPollUseCase
+import com.meloda.app.fast.data.LongPollUseCaseImpl
+import org.koin.core.module.dsl.singleOf
+import org.koin.dsl.bind
+import org.koin.dsl.module
+
+val longPollModule = module {
+ singleOf(::LongPollUseCaseImpl) bind LongPollUseCase::class
+ singleOf(::LongPollUpdatesParser)
+}
diff --git a/app/src/main/kotlin/com/meloda/fast/util/NotificationsUtils.kt b/app/src/main/kotlin/com/meloda/app/fast/util/NotificationsUtils.kt
similarity index 93%
rename from app/src/main/kotlin/com/meloda/fast/util/NotificationsUtils.kt
rename to app/src/main/kotlin/com/meloda/app/fast/util/NotificationsUtils.kt
index efb8294f..523bc569 100644
--- a/app/src/main/kotlin/com/meloda/fast/util/NotificationsUtils.kt
+++ b/app/src/main/kotlin/com/meloda/app/fast/util/NotificationsUtils.kt
@@ -1,11 +1,11 @@
-package com.meloda.fast.util
+package com.meloda.app.fast.util
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
-import com.meloda.fast.R
+import com.meloda.app.fast.designsystem.R as UiR
object NotificationsUtils {
@@ -27,7 +27,7 @@ object NotificationsUtils {
actions: List = emptyList(),
): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(context, channelId)
- .setSmallIcon(R.drawable.ic_fast_logo)
+ .setSmallIcon(UiR.drawable.ic_fast_logo)
.setContentTitle(title)
.setPriority(priority.value)
.setContentIntent(contentIntent)
@@ -69,5 +69,4 @@ object NotificationsUtils {
enum class NotificationPriority(val value: Int) {
Default(0), Low(-1), Min(-2), High(1), Max(2)
}
-
}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/ApiEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/ApiEvent.kt
deleted file mode 100644
index bbdad63b..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/ApiEvent.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api
-
-enum class ApiEvent(val value: Int) {
- MessageSetFlags(2),
- MessageClearFlags(3),
- MessageNew(4),
- MessageEdit(5),
- MessageReadIncoming(6),
- MessageReadOutgoing(7),
- MessagesDeleted(13),
- PinUnpinConversation(20),
- PrivateTyping(61),
- ChatTyping(62),
- OneMoreTyping(63),
- VoiceRecording(64),
- PhotoUploading(65),
- VideoUploading(66),
- FileUploading(67),
- UnreadCountUpdate(80)
- ;
-
- companion object {
- fun parse(value: Int) = values().firstOrNull { it.value == value }
- }
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/ApiExtensions.kt b/app/src/main/kotlin/com/meloda/fast/api/ApiExtensions.kt
deleted file mode 100644
index 83a8ee8c..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/ApiExtensions.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.meloda.fast.api
-
-object ApiExtensions {
-
- val Boolean.intString get() = (if (this) 1 else 0).toString()
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt b/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt
deleted file mode 100644
index d55a408c..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/VkUtils.kt
+++ /dev/null
@@ -1,1285 +0,0 @@
-package com.meloda.fast.api
-
-import android.content.Context
-import android.graphics.Typeface
-import android.graphics.drawable.Drawable
-import android.text.SpannableString
-import android.text.SpannableStringBuilder
-import android.text.Spanned
-import android.text.TextPaint
-import android.text.style.ClickableSpan
-import android.text.style.StyleSpan
-import android.view.View
-import androidx.core.content.ContextCompat
-import com.google.gson.Gson
-import com.meloda.fast.R
-import com.meloda.fast.api.base.ApiError
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.model.attachments.*
-import com.meloda.fast.api.model.base.BaseVkMessage
-import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
-import com.meloda.fast.api.model.domain.VkConversationDomain
-import com.meloda.fast.api.network.*
-import com.meloda.fast.ext.orDots
-import com.meloda.fast.model.base.UiImage
-import com.meloda.fast.model.base.UiText
-
-@Suppress("MemberVisibilityCanBePrivate")
-object VkUtils {
-
- fun attachmentToString(
- attachmentClass: Class,
- id: Int,
- ownerId: Int,
- withAccessKey: Boolean,
- accessKey: String?,
- ): String {
- val type = when (attachmentClass) {
- VkAudio::class.java -> "audio"
- VkFile::class.java -> "doc"
- VkVideo::class.java -> "video"
- VkPhoto::class.java -> "photo"
- VkWall::class.java -> "wall"
- else -> throw IllegalArgumentException("unknown attachment class: $attachmentClass")
- }
-
- val result = StringBuilder(type).append(ownerId).append('_').append(id)
- if (withAccessKey && !accessKey.isNullOrBlank()) {
- result.append('_')
- result.append(accessKey)
- }
- return result.toString()
- }
-
-
- fun getMessageUser(message: VkMessage, profiles: Map): VkUser? {
- return (if (!message.isUser()) null
- else profiles[message.fromId]).also { message.user = it }
- }
-
- fun getMessageActionUser(message: VkMessage, profiles: Map): VkUser? {
- return if (message.actionMemberId == null || message.actionMemberId <= 0) null
- else profiles[message.actionMemberId]
- }
-
- fun getMessageGroup(message: VkMessage, groups: Map): VkGroup? {
- return (if (!message.isGroup()) null
- else groups[message.fromId]).also { message.group = it }
- }
-
- fun getMessageActionGroup(message: VkMessage, groups: Map): VkGroup? {
- return if (message.actionMemberId == null || message.actionMemberId >= 0) null
- else groups[message.actionMemberId]
- }
-
- fun getMessageAvatar(
- message: VkMessage,
- messageUser: VkUser?,
- messageGroup: VkGroup?,
- ): String? {
- return when {
- message.isUser() -> messageUser?.photo200
- message.isGroup() -> messageGroup?.photo200
- else -> null
- }
- }
-
- fun getMessageTitle(
- message: VkMessage,
- defMessageUser: VkUser? = null,
- defMessageGroup: VkGroup? = null,
- profiles: Map? = null,
- groups: Map? = null,
- ): String? {
- val messageUser: VkUser? =
- defMessageUser ?: if (profiles == null) null
- else profiles[message.fromId]
-
- val messageGroup: VkGroup? =
- defMessageGroup ?: if (groups == null) null
- else groups[message.fromId]
-
- return when {
- message.isUser() -> messageUser?.fullName
- message.isGroup() -> messageGroup?.name
- else -> null
- }
- }
-
- fun getConversationUser(
- conversation: VkConversationDomain,
- profiles: Map
- ): VkUser? {
- return if (!conversation.isUser()) null
- else profiles[conversation.id]
- }
-
- fun getConversationGroup(
- conversation: VkConversationDomain,
- groups: Map
- ): VkGroup? {
- return if (!conversation.isGroup()) null
- else groups[conversation.id]
- }
-
- fun getConversationAvatar(
- conversation: VkConversationDomain,
- conversationUser: VkUser?,
- conversationGroup: VkGroup?,
- ): String? {
- return when {
- conversation.isAccount() -> null
- conversation.isUser() -> conversationUser?.photo200
- conversation.isGroup() -> conversationGroup?.photo200
- conversation.isChat() -> conversation.conversationPhoto
- else -> null
- }
- }
-
- fun getConversationTitle(
- context: Context,
- conversation: VkConversationDomain,
- defConversationUser: VkUser? = null,
- defConversationGroup: VkGroup? = null,
- profiles: Map? = null,
- groups: Map? = null,
- ): String? {
- val conversationUser: VkUser? =
- defConversationUser ?: if (profiles == null) null
- else getConversationUser(conversation, profiles)
-
- val conversationGroup: VkGroup? =
- defConversationGroup ?: if (groups == null) null
- else getConversationGroup(conversation, groups)
-
- return when {
- conversation.isAccount() -> context.getString(R.string.favorites)
- conversation.isChat() -> conversation.conversationTitle
- conversation.isUser() -> conversationUser?.fullName
- conversation.isGroup() -> conversationGroup?.name
- else -> null
- }
- }
-
- fun getConversationUserGroup(
- conversation: VkConversationDomain,
- profiles: Map,
- groups: Map,
- ): Pair {
- val user: VkUser? = getConversationUser(conversation, profiles)
- val group: VkGroup? = getConversationGroup(conversation, groups)
-
- return user to group
- }
-
- fun getMessageUserGroup(
- message: VkMessage?,
- profiles: Map,
- groups: Map,
- ): Pair {
- if (message == null) return null to null
-
- val user: VkUser? = getMessageUser(message, profiles)
- val group: VkGroup? = getMessageGroup(message, groups)
-
- return user to group
- }
-
- fun getMessageActionUserGroup(
- message: VkMessage?,
- profiles: Map,
- groups: Map,
- ): Pair {
- if (message == null) return null to null
-
- val user: VkUser? = getMessageActionUser(message, profiles)
- val group: VkGroup? = getMessageActionGroup(message, groups)
-
- return user to group
- }
-
- fun prepareMessageText(text: String, forConversations: Boolean = false): String {
- return text.apply {
- if (forConversations) {
- replace("\n", "")
- }
-
- replace("&", "&")
- replace(""", "\"")
- replace("
", "\n")
- replace(">", ">")
- replace("<", "<")
- replace("
", "\n")
- replace("–", "-")
- trim()
- }
- }
-
- fun isPreviousMessageSentFiveMinutesAgo(prevMessage: VkMessage?, message: VkMessage?) =
- prevMessage != null && message != null && (message.date - prevMessage.date >= 300)
-
- fun isPreviousMessageFromDifferentSender(prevMessage: VkMessage?, message: VkMessage?) =
- prevMessage != null && message != null && prevMessage.fromId != message.fromId
-
- fun parseForwards(baseForwards: List?): List? {
- if (baseForwards.isNullOrEmpty()) return null
-
- val forwards = mutableListOf()
-
- for (baseForward in baseForwards) {
- forwards += baseForward.asVkMessage()
- }
-
- return forwards
- }
-
- fun parseReplyMessage(baseReplyMessage: BaseVkMessage?): VkMessage? {
- if (baseReplyMessage == null) return null
-
- return baseReplyMessage.asVkMessage()
- }
-
- fun parseAttachments(baseAttachments: List?): List? {
- if (baseAttachments.isNullOrEmpty()) return null
-
- val attachments = mutableListOf()
-
- for (baseAttachment in baseAttachments) {
- when (baseAttachment.getPreparedType()) {
- BaseVkAttachmentItem.AttachmentType.Photo -> {
- val photo = baseAttachment.photo ?: continue
- attachments += photo.asVkPhoto()
- }
-
- BaseVkAttachmentItem.AttachmentType.Video -> {
- val video = baseAttachment.video ?: continue
- attachments += video.asVkVideo()
- }
-
- BaseVkAttachmentItem.AttachmentType.Audio -> {
- val audio = baseAttachment.audio ?: continue
- attachments += audio.asVkAudio()
- }
-
- BaseVkAttachmentItem.AttachmentType.File -> {
- val file = baseAttachment.file ?: continue
- attachments += file.asVkFile()
- }
-
- BaseVkAttachmentItem.AttachmentType.Link -> {
- val link = baseAttachment.link ?: continue
- attachments += link.asVkLink()
- }
-
- BaseVkAttachmentItem.AttachmentType.MiniApp -> {
- val miniApp = baseAttachment.miniApp ?: continue
- attachments += miniApp.asVkMiniApp()
- }
-
- BaseVkAttachmentItem.AttachmentType.Voice -> {
- val voiceMessage = baseAttachment.voiceMessage ?: continue
- attachments += voiceMessage.asVkVoiceMessage()
- }
-
- BaseVkAttachmentItem.AttachmentType.Sticker -> {
- val sticker = baseAttachment.sticker ?: continue
- attachments += sticker.asVkSticker()
- }
-
- BaseVkAttachmentItem.AttachmentType.Gift -> {
- val gift = baseAttachment.gift ?: continue
- attachments += gift.asVkGift()
- }
-
- BaseVkAttachmentItem.AttachmentType.Wall -> {
- val wall = baseAttachment.wall ?: continue
- attachments += wall.asVkWall()
- }
-
- BaseVkAttachmentItem.AttachmentType.Graffiti -> {
- val graffiti = baseAttachment.graffiti ?: continue
- attachments += graffiti.asVkGraffiti()
- }
-
- BaseVkAttachmentItem.AttachmentType.Poll -> {
- val poll = baseAttachment.poll ?: continue
- attachments += poll.asVkPoll()
- }
-
- BaseVkAttachmentItem.AttachmentType.WallReply -> {
- val wallReply = baseAttachment.wallReply ?: continue
- attachments += wallReply.asVkWallReply()
- }
-
- BaseVkAttachmentItem.AttachmentType.Call -> {
- val call = baseAttachment.call ?: continue
- attachments += call.asVkCall()
- }
-
- BaseVkAttachmentItem.AttachmentType.GroupCallInProgress -> {
- val groupCall = baseAttachment.groupCall ?: continue
- attachments += groupCall.asVkGroupCall()
- }
-
- BaseVkAttachmentItem.AttachmentType.Curator -> {
- val curator = baseAttachment.curator ?: continue
- attachments += curator.asVkCurator()
- }
-
- BaseVkAttachmentItem.AttachmentType.Event -> {
- val event = baseAttachment.event ?: continue
- attachments += event.asVkEvent()
- }
-
- BaseVkAttachmentItem.AttachmentType.Story -> {
- val story = baseAttachment.story ?: continue
- attachments += story.asVkStory()
- }
-
- BaseVkAttachmentItem.AttachmentType.Widget -> {
- val widget = baseAttachment.widget ?: continue
- attachments += widget.asVkWidget()
- }
-
- else -> continue
- }
- }
-
- return attachments
- }
-
- fun getActionMessageText(
- message: VkMessage?,
- youPrefix: String,
- messageUser: VkUser?,
- messageGroup: VkGroup?,
- action: VkMessage.Action?,
- actionUser: VkUser?,
- actionGroup: VkGroup?,
- ): UiText? {
- if (message == null) return null
-
- return when (action) {
- VkMessage.Action.CHAT_CREATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(
- R.string.message_action_chat_created,
- listOf(prefix, text)
- )
- }
-
- VkMessage.Action.CHAT_TITLE_UPDATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(
- R.string.message_action_chat_renamed,
- listOf(prefix, text)
- )
- }
-
- VkMessage.Action.CHAT_PHOTO_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_photo_update, listOf(prefix))
- }
-
- VkMessage.Action.CHAT_PHOTO_REMOVE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_photo_remove, listOf(prefix))
- }
-
- VkMessage.Action.CHAT_KICK_USER -> {
- val memberId = message.actionMemberId ?: return null
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
-
- UiText.ResourceParams(R.string.message_action_chat_user_left, listOf(prefix))
- } else {
- val prefix =
- if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString().orDots()
-
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_kicked, listOf(prefix, postfix)
- )
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER -> {
- val memberId = message.actionMemberId ?: 0
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_returned,
- listOf(prefix)
- )
- } else {
- val prefix = if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString().orDots()
-
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_invited,
- listOf(prefix, postfix)
- )
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_joined_by_link,
- listOf(prefix)
- )
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_CALL -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_joined_by_call,
- listOf(prefix)
- )
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_CALL_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(
- R.string.message_action_chat_user_joined_by_call_link,
- listOf(prefix)
- )
- }
-
- VkMessage.Action.CHAT_PIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_pin_message, listOf(prefix))
- }
-
- VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_unpin_message, listOf(prefix))
- }
-
- VkMessage.Action.CHAT_SCREENSHOT -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_screenshot, listOf(prefix))
- }
-
- VkMessage.Action.CHAT_STYLE_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- UiText.ResourceParams(R.string.message_action_chat_style_update, listOf(prefix))
- }
-
- null -> null
- }
- }
-
- fun getActionMessageText(
- context: Context,
- message: VkMessage?,
- youPrefix: String,
- messageUser: VkUser?,
- messageGroup: VkGroup?,
- action: VkMessage.Action?,
- actionUser: VkUser?,
- actionGroup: VkGroup?,
- ): SpannableString? {
- if (message == null) return null
-
- return when (action) {
- VkMessage.Action.CHAT_CREATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_created, prefix, text)
-
- val startIndex = spanText.indexOf(text, startIndex = prefix.length)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- it.setSpan(
- StyleSpan(Typeface.BOLD),
- startIndex,
- startIndex + text.length, 0
- )
- }
- }
-
- VkMessage.Action.CHAT_TITLE_UPDATE -> {
- val text = message.actionText ?: return null
-
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_renamed, prefix, text)
- val startIndex = spanText.indexOf(text)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- it.setSpan(
- StyleSpan(Typeface.BOLD), startIndex, startIndex + text.length, 0
- )
- }
- }
-
- VkMessage.Action.CHAT_PHOTO_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_photo_update, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_PHOTO_REMOVE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_photo_remove, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_KICK_USER -> {
- val memberId = message.actionMemberId ?: return null
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
-
- val spanText =
- context.getString(R.string.message_action_chat_user_left, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- } else {
- val prefix =
- if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString().orDots()
-
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
-
- val spanText =
- context.getString(
- R.string.message_action_chat_user_kicked,
- prefix,
- postfix
- )
- val startIndex = spanText.indexOf(postfix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- it.setSpan(
- StyleSpan(Typeface.BOLD), startIndex, startIndex + postfix.length, 0
- )
- }
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER -> {
- val memberId = message.actionMemberId ?: 0
- val isUser = memberId > 0
- val isGroup = memberId < 0
-
- if (isUser && actionUser == null) return null
- if (isGroup && actionGroup == null) return null
-
- if (memberId == message.fromId) {
- val prefix = if (memberId == UserConfig.userId) youPrefix
- else actionUser.toString()
-
- val spanText =
- context.getString(R.string.message_action_chat_user_returned, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- } else {
- val prefix = if (message.fromId == UserConfig.userId) youPrefix
- else messageUser?.toString() ?: messageGroup?.toString().orDots()
-
- val postfix =
- if (memberId == UserConfig.userId) youPrefix.lowercase()
- else actionUser.toString()
-
- val spanText =
- context.getString(
- R.string.message_action_chat_user_invited,
- prefix,
- postfix
- )
- val startIndex = spanText.indexOf(postfix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- it.setSpan(
- StyleSpan(Typeface.BOLD), startIndex, startIndex + postfix.length, 0
- )
- }
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_user_joined_by_link, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_CALL -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_user_joined_by_call, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_INVITE_USER_BY_CALL_LINK -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_user_joined_by_call_link, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_PIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_pin_message, prefix).trim()
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_unpin_message, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_SCREENSHOT -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isGroup() -> messageGroup?.name
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_screenshot, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- VkMessage.Action.CHAT_STYLE_UPDATE -> {
- val prefix = when {
- message.fromId == UserConfig.userId -> youPrefix
- message.isUser() -> messageUser?.toString()
- else -> return null
- } ?: return null
-
- val spanText =
- context.getString(R.string.message_action_chat_style_update, prefix)
-
- SpannableString(spanText).also {
- it.setSpan(StyleSpan(Typeface.BOLD), 0, prefix.length, 0)
- }
- }
-
- null -> null
- }
- }
-
- fun getActionConversationText(
- message: VkMessage?,
- youPrefix: String,
- messageUser: VkUser? = null,
- messageGroup: VkGroup? = null,
- action: VkMessage.Action?,
- actionUser: VkUser?,
- actionGroup: VkGroup?,
- ): UiText? {
- return getActionMessageText(
- message = message,
- youPrefix = youPrefix,
- messageUser = messageUser,
- messageGroup = messageGroup,
- action = action,
- actionUser = actionUser,
- actionGroup = actionGroup,
- )
- }
-
- fun getActionConversationText(
- context: Context,
- message: VkMessage?,
- youPrefix: String,
- messageUser: VkUser? = null,
- messageGroup: VkGroup? = null,
- action: VkMessage.Action?,
- actionUser: VkUser?,
- actionGroup: VkGroup?,
- ): String? {
- return getActionMessageText(
- context = context,
- message = message,
- youPrefix = youPrefix,
- messageUser = messageUser,
- messageGroup = messageGroup,
- action = action,
- actionUser = actionUser,
- actionGroup = actionGroup,
- )?.toString()
- }
-
- fun getForwardsText(message: VkMessage?): UiText? {
- if (message?.forwards.isNullOrEmpty()) return null
-
- return message?.forwards?.let { forwards ->
- UiText.Resource(
- if (forwards.size == 1) R.string.forwarded_message
- else R.string.forwarded_messages
- )
- }
- }
-
- fun getAttachmentText(message: VkMessage?): UiText? {
- message?.geo?.let {
- return when (it.type) {
- "point" -> UiText.Resource(R.string.message_geo_point)
- else -> UiText.Resource(R.string.message_geo)
- }
- }
- if (message?.attachments.isNullOrEmpty()) return null
-
- return message?.attachments?.let { attachments ->
- if (attachments.size == 1) {
- getAttachmentTypeByClass(attachments[0])?.let {
- getAttachmentTextByType(it)
- }
- } else {
- if (isAttachmentsHaveOneType(attachments)) {
- getAttachmentTypeByClass(attachments[0])?.let {
- getAttachmentTextByType(it, attachments.size)
- }
- } else {
- UiText.Resource(R.string.message_attachments_many)
- }
- }
- }
- }
-
- fun getAttachmentConversationIcon(message: VkMessage?): UiImage? {
- return message?.attachments?.let { attachments ->
- if (attachments.isEmpty()) return null
- if (attachments.size == 1 || isAttachmentsHaveOneType(attachments)) {
- message.geo?.let {
- return UiImage.Resource(R.drawable.ic_map_marker)
- }
-
- getAttachmentTypeByClass(attachments[0])?.let {
- getAttachmentIconByType(it)
- }
- } else {
- UiImage.Resource(R.drawable.ic_baseline_attach_file_24)
- }
- }
- }
-
- fun getAttachmentConversationIcon(
- context: Context,
- message: VkMessage?,
- ): Drawable? {
- if (message == null) return null
- return message.attachments?.let { attachments ->
- if (attachments.size == 1 || isAttachmentsHaveOneType(attachments)) {
- message.geo?.let {
- return ContextCompat.getDrawable(context, R.drawable.ic_map_marker)
- }
-
- if (attachments.isEmpty()) return null
-
- getAttachmentTypeByClass(attachments[0])?.let {
- getAttachmentIconByType(
- context,
- it
- )
- }
- } else {
- ContextCompat.getDrawable(context, R.drawable.ic_baseline_attach_file_24)
- }
- }
- }
-
- fun getAttachmentIconByType(attachmentType: BaseVkAttachmentItem.AttachmentType): UiImage? {
- return when (attachmentType) {
- BaseVkAttachmentItem.AttachmentType.Photo -> R.drawable.ic_attachment_photo
- BaseVkAttachmentItem.AttachmentType.Video -> R.drawable.ic_attachment_video
- BaseVkAttachmentItem.AttachmentType.Audio -> R.drawable.ic_attachment_audio
- BaseVkAttachmentItem.AttachmentType.File -> R.drawable.ic_attachment_file
- BaseVkAttachmentItem.AttachmentType.Link -> R.drawable.ic_attachment_link
- BaseVkAttachmentItem.AttachmentType.Voice -> R.drawable.ic_attachment_voice
- BaseVkAttachmentItem.AttachmentType.MiniApp -> R.drawable.ic_attachment_mini_app
- BaseVkAttachmentItem.AttachmentType.Sticker -> R.drawable.ic_attachment_sticker
- BaseVkAttachmentItem.AttachmentType.Gift -> R.drawable.ic_attachment_gift
- BaseVkAttachmentItem.AttachmentType.Wall -> R.drawable.ic_attachment_wall
- BaseVkAttachmentItem.AttachmentType.Graffiti -> R.drawable.ic_attachment_graffiti
- BaseVkAttachmentItem.AttachmentType.Poll -> R.drawable.ic_attachment_poll
- BaseVkAttachmentItem.AttachmentType.WallReply -> R.drawable.ic_attachment_wall_reply
- BaseVkAttachmentItem.AttachmentType.Call -> R.drawable.ic_attachment_call
- BaseVkAttachmentItem.AttachmentType.GroupCallInProgress -> R.drawable.ic_attachment_group_call
- BaseVkAttachmentItem.AttachmentType.Story -> R.drawable.ic_attachment_story
- else -> null
- }?.let(UiImage::Resource)
- }
-
- @Deprecated("Use new with UiImage")
- fun getAttachmentIconByType(
- context: Context,
- attachmentType: BaseVkAttachmentItem.AttachmentType,
- ): Drawable? {
- val resId = when (attachmentType) {
- BaseVkAttachmentItem.AttachmentType.Photo -> R.drawable.ic_attachment_photo
- BaseVkAttachmentItem.AttachmentType.Video -> R.drawable.ic_attachment_video
- BaseVkAttachmentItem.AttachmentType.Audio -> R.drawable.ic_attachment_audio
- BaseVkAttachmentItem.AttachmentType.File -> R.drawable.ic_attachment_file
- BaseVkAttachmentItem.AttachmentType.Link -> R.drawable.ic_attachment_link
- BaseVkAttachmentItem.AttachmentType.Voice -> R.drawable.ic_attachment_voice
- BaseVkAttachmentItem.AttachmentType.MiniApp -> R.drawable.ic_attachment_mini_app
- BaseVkAttachmentItem.AttachmentType.Sticker -> R.drawable.ic_attachment_sticker
- BaseVkAttachmentItem.AttachmentType.Gift -> R.drawable.ic_attachment_gift
- BaseVkAttachmentItem.AttachmentType.Wall -> R.drawable.ic_attachment_wall
- BaseVkAttachmentItem.AttachmentType.Graffiti -> R.drawable.ic_attachment_graffiti
- BaseVkAttachmentItem.AttachmentType.Poll -> R.drawable.ic_attachment_poll
- BaseVkAttachmentItem.AttachmentType.WallReply -> R.drawable.ic_attachment_wall_reply
- BaseVkAttachmentItem.AttachmentType.Call -> R.drawable.ic_attachment_call
- BaseVkAttachmentItem.AttachmentType.GroupCallInProgress -> R.drawable.ic_attachment_group_call
- BaseVkAttachmentItem.AttachmentType.Story -> R.drawable.ic_attachment_story
- else -> return null
- }
-
- return ContextCompat.getDrawable(context, resId)
- }
-
- fun isAttachmentsHaveOneType(attachments: List): Boolean {
- if (attachments.isEmpty()) return true
- if (attachments.size == 1) return true
-
- val firstType = getAttachmentTypeByClass(attachments[0])
- for (i in 1 until attachments.size) {
- val type = getAttachmentTypeByClass(attachments[i])
- if (type != firstType) return false
- }
-
- return true
- }
-
- fun getAttachmentTypeByClass(attachment: VkAttachment): BaseVkAttachmentItem.AttachmentType? {
- return when (attachment) {
- is VkPhoto -> BaseVkAttachmentItem.AttachmentType.Photo
- is VkVideo -> BaseVkAttachmentItem.AttachmentType.Video
- is VkAudio -> BaseVkAttachmentItem.AttachmentType.Audio
- is VkFile -> BaseVkAttachmentItem.AttachmentType.File
- is VkLink -> BaseVkAttachmentItem.AttachmentType.Link
- is VkMiniApp -> BaseVkAttachmentItem.AttachmentType.MiniApp
- is VkVoiceMessage -> BaseVkAttachmentItem.AttachmentType.Voice
- is VkSticker -> BaseVkAttachmentItem.AttachmentType.Sticker
- is VkGift -> BaseVkAttachmentItem.AttachmentType.Gift
- is VkWall -> BaseVkAttachmentItem.AttachmentType.Wall
- is VkGraffiti -> BaseVkAttachmentItem.AttachmentType.Graffiti
- is VkPoll -> BaseVkAttachmentItem.AttachmentType.Poll
- is VkWallReply -> BaseVkAttachmentItem.AttachmentType.WallReply
- is VkCall -> BaseVkAttachmentItem.AttachmentType.Call
- is VkGroupCall -> BaseVkAttachmentItem.AttachmentType.GroupCallInProgress
- is VkEvent -> BaseVkAttachmentItem.AttachmentType.Event
- is VkCurator -> BaseVkAttachmentItem.AttachmentType.Curator
- is VkStory -> BaseVkAttachmentItem.AttachmentType.Story
- is VkWidget -> BaseVkAttachmentItem.AttachmentType.Widget
- else -> null
- }
- }
-
- fun getAttachmentTextByType(
- attachmentType: BaseVkAttachmentItem.AttachmentType,
- size: Int = 1,
- ): UiText {
- return when (attachmentType) {
- BaseVkAttachmentItem.AttachmentType.Photo ->
- UiText.QuantityResource(R.plurals.attachment_photos, size)
-
- BaseVkAttachmentItem.AttachmentType.Video ->
- UiText.QuantityResource(R.plurals.attachment_videos, size)
-
- BaseVkAttachmentItem.AttachmentType.Audio ->
- UiText.QuantityResource(R.plurals.attachment_audios, size)
-
- BaseVkAttachmentItem.AttachmentType.File ->
- UiText.QuantityResource(R.plurals.attachment_files, size)
-
- BaseVkAttachmentItem.AttachmentType.Link ->
- UiText.Resource(R.string.message_attachments_link)
-
- BaseVkAttachmentItem.AttachmentType.Voice ->
- UiText.Resource(R.string.message_attachments_voice)
-
- BaseVkAttachmentItem.AttachmentType.MiniApp ->
- UiText.Resource(R.string.message_attachments_mini_app)
-
- BaseVkAttachmentItem.AttachmentType.Sticker ->
- UiText.Resource(R.string.message_attachments_sticker)
-
- BaseVkAttachmentItem.AttachmentType.Gift ->
- UiText.Resource(R.string.message_attachments_gift)
-
- BaseVkAttachmentItem.AttachmentType.Wall ->
- UiText.Resource(R.string.message_attachments_wall)
-
- BaseVkAttachmentItem.AttachmentType.Graffiti ->
- UiText.Resource(R.string.message_attachments_graffiti)
-
- BaseVkAttachmentItem.AttachmentType.Poll ->
- UiText.Resource(R.string.message_attachments_poll)
-
- BaseVkAttachmentItem.AttachmentType.WallReply ->
- UiText.Resource(R.string.message_attachments_wall_reply)
-
- BaseVkAttachmentItem.AttachmentType.Call ->
- UiText.Resource(R.string.message_attachments_call)
-
- BaseVkAttachmentItem.AttachmentType.GroupCallInProgress ->
- UiText.Resource(R.string.message_attachments_call_in_progress)
-
- BaseVkAttachmentItem.AttachmentType.Event ->
- UiText.Resource(R.string.message_attachments_event)
-
- BaseVkAttachmentItem.AttachmentType.Curator ->
- UiText.Resource(R.string.message_attachments_curator)
-
- BaseVkAttachmentItem.AttachmentType.Story ->
- UiText.Resource(R.string.message_attachments_story)
-
- BaseVkAttachmentItem.AttachmentType.Widget ->
- UiText.Resource(R.string.message_attachments_widget)
-
- else -> UiText.Simple(attachmentType.value)
- }
- }
-
- fun getApiError(gson: Gson, errorString: String?): ApiAnswer.Error {
- try {
- val defaultError = gson.fromJson(errorString, ApiError::class.java)
-
- val error: ApiError =
- when (defaultError.error) {
- VkErrorCodes.UserAuthorizationFailed.toString() -> {
- val authorizationError =
- gson.fromJson(errorString, AuthorizationError::class.java)
-
- authorizationError
- }
-
- VkErrorCodes.AccessTokenExpired.toString() -> {
- val tokenExpiredError =
- gson.fromJson(errorString, TokenExpiredError::class.java)
-
- tokenExpiredError
- }
-
- VkErrors.NeedValidation -> {
- val validationError =
- gson.fromJson(
- errorString,
- if (defaultError.errorMessage == VkErrorMessages.UserBanned) {
- UserBannedError::class.java
- } else {
- ValidationRequiredError::class.java
- }
- )
-
- validationError
- }
-
- VkErrors.NeedCaptcha -> {
- val captchaRequiredError =
- gson.fromJson(errorString, CaptchaRequiredError::class.java)
-
- captchaRequiredError
- }
-
- VkErrors.InvalidRequest -> {
- when (defaultError.errorType) {
- VkErrorTypes.OtpFormatIncorrect -> WrongTwoFaCodeFormatError
- VkErrorTypes.WrongOtp -> WrongTwoFaCodeError
- else -> defaultError
- }
- }
-
- else -> defaultError
- }
-
- return ApiAnswer.Error(error)
- } catch (e: Exception) {
- return ApiAnswer.Error(ApiError(throwable = e))
- }
- }
-
- fun visualizeMentions(
- messageText: String,
- mentionColor: Int,
- onMentionClick: ((id: Int) -> Unit)? = null,
- ): SpannableStringBuilder {
- if (messageText.isEmpty()) {
- return SpannableStringBuilder("")
- }
-
- var newMessageText = messageText
-
- val idsIndexes = mutableListOf>()
- val mentions = mutableListOf>()
-
- var startFrom = 0
-
- while (true) {
- val leftBracketIndex = newMessageText.indexOf('[', startFrom)
- val verticalLineIndex = newMessageText.indexOf('|', startFrom)
- val rightBracketIndex = newMessageText.indexOf(']', startFrom)
-
- if (leftBracketIndex == -1 ||
- verticalLineIndex == -1 ||
- rightBracketIndex == -1
- ) break
-
- val idPart = newMessageText.substring(leftBracketIndex + 1, verticalLineIndex)
-
- val actualId = idPart.substring(2, idPart.length).toIntOrNull() ?: -1
-
- if (!idPart.matches(Regex("^id(\\d+)\$")) || rightBracketIndex - verticalLineIndex < 2) {
- break
- }
-
- val text = newMessageText.substring(verticalLineIndex + 1, rightBracketIndex)
-
- val str = "[$idPart|$text]"
-
- mentions += str to text
-
- idsIndexes += Triple(actualId, leftBracketIndex, leftBracketIndex + text.length)
-
- startFrom = rightBracketIndex + 1
- }
-
- idsIndexes.reverse()
-
- mentions.forEachIndexed { index, pair ->
- val old = pair.first
- val new = pair.second
-
- val oldIndexStart = newMessageText.indexOf(old)
-
- idsIndexes[index].copy(
- second = oldIndexStart,
- third = oldIndexStart + new.length
- ).let { idsIndexes[index] = it }
-
- newMessageText = newMessageText.replace(old, new)
- }
-
- val spanBuilder = SpannableStringBuilder(newMessageText)
-
- idsIndexes.forEach { triple ->
- val id = triple.first
- val start = triple.second
- val end = triple.third
-
- spanBuilder.setSpan(
- createClickableSpan(id, mentionColor, onMentionClick),
- start,
- end,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
- )
- }
-
- return spanBuilder
- }
-
- private fun createClickableSpan(
- id: Int,
- mentionColor: Int,
- onMentionClick: ((id: Int) -> Unit)? = null,
- ): ClickableSpan {
- return object : ClickableSpan() {
- override fun onClick(widget: View) {
- widget.cancelPendingInputEvents()
-
- onMentionClick?.invoke(id)
- }
-
- override fun updateDrawState(ds: TextPaint) {
- ds.color = mentionColor
-// ds.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt b/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt
deleted file mode 100644
index ae38560f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/base/ApiError.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.meloda.fast.api.base
-
-import com.google.gson.Gson
-import com.google.gson.annotations.SerializedName
-import okio.IOException
-
-open class ApiError(
- @SerializedName("error", alternate = ["error_code"])
- val error: String? = null,
- @SerializedName("error_msg", alternate = ["error_description"])
- open val errorMessage: String? = null,
- @SerializedName("error_type")
- val errorType: String? = null,
- val throwable: Throwable? = null
-) : IOException() {
-
- override fun toString(): String {
- return Gson().toJson(this)
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt
deleted file mode 100644
index 51c3cd35..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/base/ApiResponse.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.meloda.fast.api.base
-
-data class ApiResponse(
- val error: ApiError? = null,
- val response: T? = null
-) {
- val isSuccessful get() = error == null && response != null
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/base/AttachmentClassNameIsEmptyException.kt b/app/src/main/kotlin/com/meloda/fast/api/base/AttachmentClassNameIsEmptyException.kt
deleted file mode 100644
index 2aad4520..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/base/AttachmentClassNameIsEmptyException.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.api.base
-
-import com.meloda.fast.api.model.attachments.VkAttachment
-import okio.IOException
-
-class AttachmentClassNameIsEmptyException(attachment: VkAttachment) :
- IOException(
- "attachment ${attachment.javaClass.name} does not have declared field \"className\""
- )
diff --git a/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollEvent.kt
deleted file mode 100644
index 3637bd5f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollEvent.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.meloda.fast.api.longpoll
-
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-
-sealed class LongPollEvent {
-
- data class VkMessageNewEvent(
- val message: VkMessage,
- val profiles: HashMap,
- val groups: HashMap,
- ) : LongPollEvent()
-
- data class VkMessageEditEvent(val message: VkMessage) : LongPollEvent()
-
- data class VkMessageReadIncomingEvent(
- val peerId: Int,
- val messageId: Int,
- val unreadCount: Int,
- ) : LongPollEvent()
-
- data class VkMessageReadOutgoingEvent(
- val peerId: Int,
- val messageId: Int,
- val unreadCount: Int,
- ) : LongPollEvent()
-
- data class VkConversationPinStateChangedEvent(
- val peerId: Int,
- val majorId: Int,
- ) : LongPollEvent()
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollUpdatesParser.kt b/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollUpdatesParser.kt
deleted file mode 100644
index 324d446f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/longpoll/LongPollUpdatesParser.kt
+++ /dev/null
@@ -1,300 +0,0 @@
-package com.meloda.fast.api.longpoll
-
-import android.util.Log
-import com.google.gson.JsonArray
-import com.meloda.fast.api.ApiEvent
-import com.meloda.fast.api.VKConstants
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.messages.MessagesGetByIdRequest
-import com.meloda.fast.base.viewmodel.VkEventCallback
-import com.meloda.fast.data.messages.MessagesRepository
-import kotlinx.coroutines.CoroutineExceptionHandler
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-
-@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate")
-class LongPollUpdatesParser(private val messagesRepository: MessagesRepository) : CoroutineScope {
-
- private val job = SupervisorJob()
-
- private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
- Log.d("LongPollUpdatesParser", "error: $throwable")
- throwable.printStackTrace()
- }
-
- override val coroutineContext: CoroutineContext
- get() = Dispatchers.Default + job + exceptionHandler
-
- private val listenersMap: MutableMap>> =
- mutableMapOf()
-
- fun parseNextUpdate(event: JsonArray) {
- val eventId = event[0].asInt
- val eventType: ApiEvent? = ApiEvent.parse(eventId)
-
- if (eventType == null) {
- Log.d("LongPollUpdatesParser", "parseNextUpdate: unknownEvent: $event")
- return
- }
-
- when (eventType) {
- ApiEvent.MessageSetFlags -> parseMessageSetFlags(eventType, event)
- ApiEvent.MessageClearFlags -> parseMessageClearFlags(eventType, event)
- ApiEvent.MessageNew -> parseMessageNew(eventType, event)
- ApiEvent.MessageEdit -> parseMessageEdit(eventType, event)
- ApiEvent.MessageReadIncoming -> parseMessageReadIncoming(eventType, event)
- ApiEvent.MessageReadOutgoing -> parseMessageReadOutgoing(eventType, event)
- ApiEvent.MessagesDeleted -> parseMessagesDeleted(eventType, event)
- ApiEvent.PinUnpinConversation -> parseConversationPinStateChanged(eventType, event)
- ApiEvent.PrivateTyping -> onNewEvent(eventType, event)
- ApiEvent.ChatTyping -> onNewEvent(eventType, event)
- ApiEvent.OneMoreTyping -> onNewEvent(eventType, event)
- ApiEvent.VoiceRecording -> onNewEvent(eventType, event)
- ApiEvent.PhotoUploading -> onNewEvent(eventType, event)
- ApiEvent.VideoUploading -> onNewEvent(eventType, event)
- ApiEvent.FileUploading -> onNewEvent(eventType, event)
- ApiEvent.UnreadCountUpdate -> onNewEvent(eventType, event)
- }
-
- }
-
- private fun onNewEvent(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "newEvent: $eventType: $event")
- }
-
- private fun parseConversationPinStateChanged(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
-
- val peerId = event[1].asInt
- val majorId = event[2].asInt
-
- launch {
- listenersMap[ApiEvent.PinUnpinConversation]?.let { listeners ->
- listeners.forEach { vkEventCallback ->
- (vkEventCallback as VkEventCallback)
- .onEvent(
- LongPollEvent.VkConversationPinStateChangedEvent(
- peerId = peerId,
- majorId = majorId
- )
- )
- }
- }
- }
- }
-
- private fun parseMessageSetFlags(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- }
-
- private fun parseMessageClearFlags(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- }
-
- private fun parseMessageNew(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- val messageId = event[1].asInt
-
- launch {
- val newMessageEvent: LongPollEvent.VkMessageNewEvent =
- loadNormalMessage(
- eventType,
- messageId
- )
-
- listenersMap[ApiEvent.MessageNew]?.let {
- it.map { vkEventCallback ->
- (vkEventCallback as VkEventCallback)
- .onEvent(newMessageEvent)
- }
- }
- }
- }
-
- private fun parseMessageEdit(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- val messageId = event[1].asInt
-
- launch {
- val editedMessageEvent: LongPollEvent.VkMessageEditEvent =
- loadNormalMessage(
- eventType,
- messageId
- )
-
- listenersMap[ApiEvent.MessageEdit]?.let {
- it.map { vkEventCallback ->
- (vkEventCallback as VkEventCallback)
- .onEvent(editedMessageEvent)
- }
- }
- }
- }
-
- private fun parseMessageReadIncoming(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- val peerId = event[1].asInt
- val messageId = event[2].asInt
- val unreadCount = event[3].asInt
-
- launch {
- listenersMap[ApiEvent.MessageReadIncoming]?.let { listeners ->
- listeners.map { vkEventCallback ->
- (vkEventCallback as VkEventCallback)
- .onEvent(
- LongPollEvent.VkMessageReadIncomingEvent(
- peerId = peerId,
- messageId = messageId,
- unreadCount = unreadCount
- )
- )
- }
- }
- }
- }
-
- private fun parseMessageReadOutgoing(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- val peerId = event[1].asInt
- val messageId = event[2].asInt
- val unreadCount = event[3].asInt
-
- launch {
- listenersMap[ApiEvent.MessageReadOutgoing]?.let { listeners ->
- listeners.map { vkEventCallback ->
- (vkEventCallback as VkEventCallback)
- .onEvent(
- LongPollEvent.VkMessageReadOutgoingEvent(
- peerId = peerId,
- messageId = messageId,
- unreadCount = unreadCount
- )
- )
- }
- }
- }
- }
-
- private fun parseMessagesDeleted(eventType: ApiEvent, event: JsonArray) {
- Log.d("LongPollUpdatesParser", "$eventType: $event")
- }
-
- private suspend fun loadNormalMessage(eventType: ApiEvent, messageId: Int) =
- coroutineScope {
- suspendCoroutine {
- launch {
- val normalMessageResponse = messagesRepository.getById(
- MessagesGetByIdRequest(
- messagesIds = listOf(messageId),
- extended = true,
- fields = VKConstants.ALL_FIELDS
- )
- )
-
- if (normalMessageResponse.isError()) {
- normalMessageResponse.error.throwable?.run { throw this }
- }
-
- val messagesResponse =
- (normalMessageResponse as? ApiAnswer.Success)?.data?.response
- ?: return@launch
-
- val messagesList = messagesResponse.items
- if (messagesList.isEmpty()) return@launch
-
- val normalMessage = messagesList[0].asVkMessage()
- messagesRepository.store(listOf(normalMessage))
-
- val profiles = hashMapOf()
- messagesResponse.profiles?.forEach { baseUser ->
- baseUser.mapToDomain().let { user -> profiles[user.id] = user }
- }
-
- val groups = hashMapOf()
- messagesResponse.groups?.forEach { baseGroup ->
- baseGroup.mapToDomain().let { group -> groups[group.id] = group }
- }
-
- val resumeValue: LongPollEvent? = when (eventType) {
- ApiEvent.MessageNew ->
- LongPollEvent.VkMessageNewEvent(
- normalMessage,
- profiles,
- groups
- )
- ApiEvent.MessageEdit -> LongPollEvent.VkMessageEditEvent(normalMessage)
- else -> null
- }
-
- resumeValue?.let { value -> it.resume(value as T) }
- }
- }
- }
-
-
- private fun registerListener(eventType: ApiEvent, listener: VkEventCallback) {
- listenersMap.let { map ->
- map[eventType] = (map[eventType] ?: mutableListOf()).also {
- it.add(listener)
- }
- }
- }
-
- fun onConversationPinStateChanged(listener: VkEventCallback) {
- registerListener(ApiEvent.PinUnpinConversation, listener)
- }
-
- fun onConversationPinStateChanged(block: (LongPollEvent.VkConversationPinStateChangedEvent) -> Unit) {
- onConversationPinStateChanged(assembleEventCallback(block))
- }
-
- fun onMessageIncomingRead(listener: VkEventCallback) {
- registerListener(ApiEvent.MessageReadIncoming, listener)
- }
-
- fun onMessageIncomingRead(block: (LongPollEvent.VkMessageReadIncomingEvent) -> Unit) {
- onMessageIncomingRead(assembleEventCallback(block))
- }
-
- fun onMessageOutgoingRead(listener: VkEventCallback) {
- registerListener(ApiEvent.MessageReadOutgoing, listener)
- }
-
- fun onMessageOutgoingRead(block: (LongPollEvent.VkMessageReadOutgoingEvent) -> Unit) {
- onMessageOutgoingRead(assembleEventCallback(block))
- }
-
- fun onNewMessage(listener: VkEventCallback) {
- registerListener(ApiEvent.MessageNew, listener)
- }
-
- fun onNewMessage(block: (LongPollEvent.VkMessageNewEvent) -> Unit) {
- onNewMessage(assembleEventCallback(block))
- }
-
- fun onMessageEdited(listener: VkEventCallback) {
- registerListener(ApiEvent.MessageEdit, listener)
- }
-
- fun onMessageEdited(block: (LongPollEvent.VkMessageEditEvent) -> Unit) {
- onMessageEdited(assembleEventCallback(block))
- }
-
- fun clearListeners() {
- listenersMap.clear()
- }
-}
-
-internal inline fun assembleEventCallback(
- crossinline block: (R) -> Unit,
-): VkEventCallback {
- return VkEventCallback { event -> block.invoke(event) }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/ActionState.kt b/app/src/main/kotlin/com/meloda/fast/api/model/ActionState.kt
deleted file mode 100644
index ece31811..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/ActionState.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.meloda.fast.api.model
-
-sealed class ActionState {
- object Phantom : ActionState()
- object CallInProgress : ActionState()
- object None : ActionState()
-
- companion object {
- fun parse(isPhantom: Boolean, isCallInProgress: Boolean): ActionState {
- return when {
- isPhantom -> Phantom
- isCallInProgress -> CallInProgress
- else -> None
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/ConversationPeerType.kt b/app/src/main/kotlin/com/meloda/fast/api/model/ConversationPeerType.kt
deleted file mode 100644
index 25c3bb53..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/ConversationPeerType.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.meloda.fast.api.model
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-sealed class ConversationPeerType : Parcelable {
- object User : ConversationPeerType()
- object Group : ConversationPeerType()
- object Chat : ConversationPeerType()
-
- fun isUser() = this == User
- fun isGroup() = this == Group
- fun isChat() = this == Chat
-
- companion object {
- fun parse(type: String): ConversationPeerType {
- return when (type) {
- "user" -> User
- "group" -> Group
- else -> Chat
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkChatMember.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkChatMember.kt
deleted file mode 100644
index 1a513309..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VkChatMember.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.meloda.fast.api.model
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkChatMember(
- val memberId: Int,
- val invitedBy: Int,
- val joinDate: Int,
- val isAdmin: Boolean,
- val isOwner: Boolean,
- val canKick: Boolean
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt
deleted file mode 100644
index 0e3469a4..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VkGroup.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.api.model
-
-import android.os.Parcelable
-import androidx.room.Entity
-import androidx.room.PrimaryKey
-import kotlinx.parcelize.Parcelize
-
-@Entity(tableName = "groups")
-@Parcelize
-data class VkGroup(
- @PrimaryKey(autoGenerate = false)
- val id: Int,
- val name: String,
- val screenName: String,
- val photo200: String?,
- val membersCount: Int?
-) : Parcelable {
-
- override fun toString() = name.trim()
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt
deleted file mode 100644
index 55373d72..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VkMessage.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.meloda.fast.api.model
-
-import androidx.room.Entity
-import androidx.room.Ignore
-import androidx.room.PrimaryKey
-import com.meloda.fast.api.UserConfig
-import com.meloda.fast.api.VKConstants
-import com.meloda.fast.api.model.attachments.VkAttachment
-import com.meloda.fast.api.model.base.BaseVkMessage
-import com.meloda.fast.api.model.domain.VkConversationDomain
-import com.meloda.fast.model.SelectableItem
-import com.meloda.fast.util.TimeUtils
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-// TODO: 05.08.2023, Danil Nikolaev: create other class for storing in database
-@Entity(tableName = "messages")
-@Parcelize
-data class VkMessage constructor(
- @PrimaryKey(autoGenerate = false)
- var id: Int,
- var text: String? = null,
- val isOut: Boolean,
- val peerId: Int,
- val fromId: Int,
- val date: Int,
- val randomId: Int,
- val action: String? = null,
- val actionMemberId: Int? = null,
- val actionText: String? = null,
- val actionConversationMessageId: Int? = null,
- val actionMessage: String? = null,
-
- var updateTime: Int? = null,
-
- var important: Boolean = false,
-
- var forwards: List? = null,
- var attachments: List? = null,
- var replyMessage: VkMessage? = null,
-
- val geo: BaseVkMessage.Geo? = null,
-) : SelectableItem() {
-
- @Ignore
- @IgnoredOnParcel
- var user: VkUser? = null
-
- @Ignore
- @IgnoredOnParcel
- var group: VkGroup? = null
-
- @Ignore
- @IgnoredOnParcel
- var actionUser: VkUser? = null
-
- @Ignore
- @IgnoredOnParcel
- var actionGroup: VkGroup? = null
-
- @Ignore
- @IgnoredOnParcel
- var state: State = State.Sent
-
- fun isPeerChat() = peerId > 2_000_000_000
-
- fun isUser() = fromId > 0
-
- fun isGroup() = fromId < 0
-
- fun isRead(conversation: VkConversationDomain) =
- if (isOut) {
- conversation.outRead - id >= 0
- } else {
- conversation.inRead - id >= 0
- }
-
- fun getPreparedAction(): Action? {
- if (action == null) return null
- return Action.parse(action)
- }
-
- fun canEdit() =
- fromId == UserConfig.userId &&
- (attachments == null ||
- !VKConstants.restrictedToEditAttachments.contains(
- requireNotNull(attachments).first().javaClass
- )) &&
- (System.currentTimeMillis() / 1000 - date.toLong() < TimeUtils.OneDayInSeconds)
-
- fun hasAttachments(): Boolean = !attachments.isNullOrEmpty()
-
- fun hasReply(): Boolean = replyMessage != null
-
- fun hasForwards(): Boolean = !forwards.isNullOrEmpty()
-
- fun hasGeo(): Boolean = geo != null
-
- fun isUpdated(): Boolean = updateTime != null && requireNotNull(updateTime) > 0
-
- fun isSending(): Boolean = state == State.Sending
-
- fun isError(): Boolean = state == State.Error
-
- fun isSent(): Boolean = state == State.Sent
-
- enum class Action(val value: String) {
- CHAT_CREATE("chat_create"),
- CHAT_PHOTO_UPDATE("chat_photo_update"),
- CHAT_PHOTO_REMOVE("chat_photo_remove"),
- CHAT_TITLE_UPDATE("chat_title_update"),
- CHAT_PIN_MESSAGE("chat_pin_message"),
- CHAT_UNPIN_MESSAGE("chat_unpin_message"),
- CHAT_INVITE_USER("chat_invite_user"),
- CHAT_INVITE_USER_BY_LINK("chat_invite_user_by_link"),
- CHAT_KICK_USER("chat_kick_user"),
- CHAT_SCREENSHOT("chat_screenshot"),
-
- CHAT_INVITE_USER_BY_CALL("chat_invite_user_by_call"),
- CHAT_INVITE_USER_BY_CALL_LINK("chat_invite_user_by_call_join_link"),
- CHAT_STYLE_UPDATE("conversation_style_update");
-
- companion object {
- fun parse(value: String?): Action? = values().firstOrNull { it.value == value }
- }
- }
-
- enum class State {
- Sending, Sent, Error
- }
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt
deleted file mode 100644
index fdd2f9e2..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/VkUser.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.model
-
-import android.os.Parcelable
-import androidx.room.Entity
-import androidx.room.PrimaryKey
-import kotlinx.parcelize.Parcelize
-
-@Entity(tableName = "users")
-@Parcelize
-data class VkUser(
- @PrimaryKey(autoGenerate = false)
- val id: Int,
- val firstName: String,
- val lastName: String,
- val online: Boolean,
- val photo200: String?,
- val lastSeen: Int?,
- val lastSeenStatus: String?,
- val birthday: String?
-) : Parcelable {
-
- override fun toString() = fullName
-
- val fullName get() = "$firstName $lastName".trim()
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt
deleted file mode 100644
index 71702bfb..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAttachment.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import android.os.Parcelable
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-open class VkAttachment : Parcelable {
-
- open fun asString(withAccessKey: Boolean = true) = ""
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
deleted file mode 100644
index 1427cf2f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkAudio.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import com.meloda.fast.api.VkUtils
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkAudio(
- val id: Int,
- val ownerId: Int,
- val title: String,
- val artist: String,
- val url: String,
- val duration: Int,
- val accessKey: String?
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
- override fun asString(withAccessKey: Boolean) = VkUtils.attachmentToString(
- attachmentClass = this::class.java,
- id = id,
- ownerId = ownerId,
- withAccessKey = withAccessKey,
- accessKey = accessKey
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt
deleted file mode 100644
index 7a6c4002..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCall.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkCall(
- val initiatorId: Int,
- val receiverId: Int,
- val state: String,
- val time: Int,
- val duration: Int,
- val isVideo: Boolean
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt
deleted file mode 100644
index dd656e4d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkCurator.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkCurator(
- val id: Int,
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt
deleted file mode 100644
index debda085..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkEvent.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkEvent(
- val id: Int
-) : VkAttachment()
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
deleted file mode 100644
index 2766e61b..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkFile.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import com.meloda.fast.api.VkUtils
-import com.meloda.fast.api.model.base.attachments.BaseVkFile
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkFile(
- val id: Int,
- val ownerId: Int,
- val title: String,
- val ext: String,
- val size: Int,
- val url: String,
- val accessKey: String?,
- val preview: BaseVkFile.Preview?
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
- override fun asString(withAccessKey: Boolean) = VkUtils.attachmentToString(
- attachmentClass = this::class.java,
- id = id,
- ownerId = ownerId,
- withAccessKey = withAccessKey,
- accessKey = accessKey
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt
deleted file mode 100644
index 6be29fca..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGift.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkGift(
- val id: Int,
- val thumb256: String?,
- val thumb96: String?,
- val thumb48: String
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt
deleted file mode 100644
index 2f4ddae9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGraffiti.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkGraffiti(
- val id: Int,
- val ownerId: Int,
- val url: String,
- val width: Int,
- val height: Int,
- val accessKey: String
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt
deleted file mode 100644
index 2a0aa581..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkGroupCall.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkGroupCall(
- val initiatorId: Int
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
deleted file mode 100644
index 321c71ac..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkLink.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkLink(
- val url: String,
- val title: String?,
- val caption: String?,
- val photo: VkPhoto?,
- val target: String?,
- val isFavorite: Boolean
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt
deleted file mode 100644
index a65fb1f1..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkMiniApp.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkMiniApp(
- val link: String
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt
deleted file mode 100644
index 4bee6a71..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkPoll.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkPoll(
- val id: Int
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
deleted file mode 100644
index da6c4c82..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkSticker.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import com.meloda.fast.api.model.base.attachments.BaseVkSticker
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkSticker(
- val id: Int,
- val productId: Int,
- val images: List,
- val backgroundImages: List
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
- fun urlForSize(size: Int): String? {
- for (image in images) {
- if (image.width == size) return image.url
- }
-
- return null
- }
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt
deleted file mode 100644
index c6862706..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkStory.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkStory(
- val id: Int,
- val ownerId: Int,
- val date: Int,
- val photo: VkPhoto?
-) : VkAttachment() {
-
- fun isFromUser() = ownerId > 0
-
- fun isFromGroup() = ownerId < 0
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt
deleted file mode 100644
index 557f4c77..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkVoiceMessage.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkVoiceMessage(
- val id: Int,
- val ownerId: Int,
- val duration: Int,
- val waveform: List,
- val linkOgg: String,
- val linkMp3: String,
- val accessKey: String,
- val transcriptState: String?,
- val transcript: String?
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
deleted file mode 100644
index bd24073a..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWall.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkWall(
- val id: Int,
- val fromId: Int,
- val toId: Int,
- val date: Int,
- val text: String,
- val attachments: List?,
- val comments: Int?,
- val likes: Int?,
- val reposts: Int?,
- val views: Int?,
- val isFavorite: Boolean,
- val accessKey: String?
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt
deleted file mode 100644
index 110145eb..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWallReply.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkWallReply(
- val id: Int
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWidget.kt b/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWidget.kt
deleted file mode 100644
index 7edb329f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/attachments/VkWidget.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.model.attachments
-
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VkWidget(
- val id: Int
-) : VkAttachment() {
-
- @IgnoredOnParcel
- val className: String = this::class.java.name
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChat.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChat.kt
deleted file mode 100644
index 1f10f4c5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChat.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.VkChat
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkChat(
- val type: String,
- val title: String,
- val admin_id: Int,
- val members_count: Int,
- val id: Int,
- val photo_50: String,
- val photo_100: String,
- val photo_200: String,
- val is_default_photo: Boolean,
- val push_settings: PushSettings
-) : Parcelable {
-
- fun asVkChat() = VkChat(
- type = type,
- title = title,
- adminId = admin_id,
- membersCount = members_count,
- id = id,
- photo50 = photo_50,
- photo100 = photo_100,
- photo200 = photo_200,
- isDefaultPhoto = is_default_photo
- )
-
- @Parcelize
- data class PushSettings(
- val sound: Int,
- val disabled_until: Int
- ) : Parcelable
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChatMember.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChatMember.kt
deleted file mode 100644
index 139664d9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkChatMember.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.VkChatMember
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkChatMember(
- val member_id: Int,
- val invited_by: Int,
- val join_date: Int,
- val is_admin: Boolean?,
- val is_owner: Boolean?,
- val can_kick: Boolean?
-) : Parcelable {
-
- fun asVkChatMember() = VkChatMember(
- memberId = member_id,
- invitedBy = invited_by,
- joinDate = join_date,
- isAdmin = is_admin == true,
- isOwner = is_owner == true,
- canKick = can_kick == true
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
deleted file mode 100644
index e76b3ea7..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkGroup.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.VkGroup
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkGroup(
- val id: Int,
- val name: String,
- val screen_name: String,
- val is_closed: Int,
- val type: String,
- val is_admin: Int,
- val is_member: Int,
- val is_advertiser: Int,
- val photo_50: String?,
- val photo_100: String?,
- val photo_200: String?,
- val members_count: Int?
-) : Parcelable {
-
- fun mapToDomain() = VkGroup(
- id = -id,
- name = name,
- screenName = screen_name,
- photo200 = photo_200,
- membersCount = members_count
- )
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkLongPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkLongPoll.kt
deleted file mode 100644
index cf5d0814..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkLongPoll.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkLongPoll(
- val server: String,
- val key: String,
- val ts: Int,
- val pts: Int
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
deleted file mode 100644
index 46a5b7fa..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkMessage.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import com.meloda.fast.api.VkUtils
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.base.attachments.BaseVkAttachmentItem
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkMessage(
- val id: Int,
- val peer_id: Int,
- val date: Int,
- val from_id: Int,
- val out: Int,
- val text: String,
- val conversation_message_id: Int,
- val fwd_messages: List? = emptyList(),
- val important: Boolean,
- val random_id: Int,
- val attachments: List = emptyList(),
- val is_hidden: Boolean,
- val payload: String,
- val geo: Geo?,
- val action: Action?,
- val ttl: Int,
- val reply_message: BaseVkMessage?,
- val update_time: Int?
-) : Parcelable {
-
- fun asVkMessage() = VkMessage(
- id = id,
- text = text.ifBlank { null },
- isOut = out == 1,
- peerId = peer_id,
- fromId = from_id,
- date = date,
- randomId = random_id,
- action = action?.type,
- actionMemberId = action?.member_id,
- actionText = action?.text,
- actionConversationMessageId = action?.conversation_message_id,
- actionMessage = action?.message,
- geo = geo,
- important = important,
- updateTime = update_time
- ).also {
- it.attachments = VkUtils.parseAttachments(attachments)
- it.forwards = VkUtils.parseForwards(fwd_messages)
- it.replyMessage = VkUtils.parseReplyMessage(reply_message)
- }
-
- @Parcelize
- data class Geo(
- val type: String,
- val coordinates: Coordinates,
- val place: Place
- ) : Parcelable {
-
- @Parcelize
- data class Coordinates(val latitude: Float, val longitude: Float) : Parcelable
-
- @Parcelize
- data class Place(val country: String, val city: String, val title: String) : Parcelable
- }
-
- @Parcelize
- data class Action(
- val type: String,
- val member_id: Int?,
- val text: String?,
- val conversation_message_id: Int?,
- val message: String?
- ) : Parcelable
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
deleted file mode 100644
index 322ce4d2..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/BaseVkUser.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.meloda.fast.api.model.base
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.VkUser
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkUser(
- val id: Int,
- val first_name: String,
- val last_name: String,
- val can_access_closed: Boolean,
- val is_closed: Boolean,
- val can_invite_to_chats: Boolean,
- val sex: Int?,
- val photo_50: String?,
- val photo_100: String?,
- val photo_200: String?,
- val online: Int?,
- val online_info: OnlineInfo?,
- val screen_name: String,
- val bdate: String?
- //...other fields
-) : Parcelable {
-
- @Parcelize
- data class OnlineInfo(
- val visible: Boolean,
- val status: String,
- val last_seen: Int?,
- val is_online: Boolean?,
- val online_mobile: Boolean?,
- val app_id: Int?
- ) : Parcelable
-
- fun mapToDomain() = VkUser(
- id = id,
- firstName = first_name,
- lastName = last_name,
- online = online == 1,
- photo200 = photo_200,
- lastSeen = online_info?.last_seen,
- lastSeenStatus = online_info?.status,
- birthday = bdate
- )
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
deleted file mode 100644
index 51f0138f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAttachmentItem.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import android.util.Log
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkAttachmentItem(
- val type: String,
- val photo: BaseVkPhoto?,
- val video: BaseVkVideo?,
- val audio: BaseVkAudio?,
- @SerializedName("doc")
- val file: BaseVkFile?,
- val link: BaseVkLink?,
- @SerializedName("mini_app")
- val miniApp: BaseVkMiniApp?,
- @SerializedName("audio_message")
- val voiceMessage: BaseVkVoiceMessage?,
- val sticker: BaseVkSticker?,
- val gift: BaseVkGift?,
- val wall: BaseVkWall?,
- val graffiti: BaseVkGraffiti?,
- val poll: BaseVkPoll?,
- @SerializedName("wall_reply")
- val wallReply: BaseVkWallReply?,
- val call: BaseVkCall?,
- @SerializedName("group_call_in_progress")
- val groupCall: BaseVkGroupCall?,
- val curator: BaseVkCurator?,
- val event: BaseVkEvent?,
- val story: BaseVkStory?,
- val widget: BaseVkWidget?
-) : Parcelable {
-
- fun getPreparedType() = AttachmentType.parse(type)
-
- enum class AttachmentType(var value: String) {
- Unknown("unknown"),
- Photo("photo"),
- Video("video"),
- Audio("audio"),
- File("doc"),
- Link("link"),
- Voice("audio_message"),
- MiniApp("mini_app"),
- Sticker("sticker"),
- Gift("gift"),
- Wall("wall"),
- Graffiti("graffiti"),
- Poll("poll"),
- WallReply("wall_reply"),
- Call("call"),
- GroupCallInProgress("group_call_in_progress"),
- Curator("curator"),
- Event("event"),
- Story("story"),
- Widget("widget")
- ;
-
- companion object {
- fun parse(value: String): AttachmentType {
- val parsedValue = values().firstOrNull { it.value == value } ?: Unknown
-
- if (parsedValue == Unknown) {
- Log.e("AttachmentType", "Unknown attachment type: $value")
- }
-
- return parsedValue
- }
- }
- }
-
-}
-
-abstract class BaseVkAttachment : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
deleted file mode 100644
index 09de47b9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkAudio.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkAudio
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkAudio(
- val id: Int,
- val title: String,
- val artist: String,
- val duration: Int,
- val url: String,
- val date: Int,
- val owner_id: Int,
- val access_key: String?,
- val is_explicit: Boolean,
- val is_focus_track: Boolean,
- val is_licensed: Boolean,
- val track_code: String,
- val genre_id: Int,
- val album: Album,
- val short_videos_allowed: Boolean,
- val stories_allowed: Boolean,
- val stories_cover_allowed: Boolean
-) : BaseVkAttachment() {
-
- fun asVkAudio() = VkAudio(
- id = id,
- ownerId = owner_id,
- title = title,
- artist = artist,
- url = url,
- duration = duration,
- accessKey = access_key
- )
-
- @Parcelize
- data class Album(
- val id: Int,
- val title: String,
- val owner_id: Int,
- val access_key: String,
- val thumb: Thumb
- ) : Parcelable {
-
- @Parcelize
- data class Thumb(
- val width: Int,
- val height: Int,
- val photo_34: String,
- val photo_68: String,
- val photo_135: String,
- val photo_270: String,
- val photo_300: String,
- val photo_600: String,
- val photo_1200: String
- ) : Parcelable
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
deleted file mode 100644
index 2bbde082..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCall.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkCall
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkCall(
- val initiator_id: Int,
- val receiver_id: Int,
- val state: String,
- val time: Int,
- val duration: Int,
- val video: Boolean
-) : Parcelable {
-
- fun asVkCall() = VkCall(
- initiatorId = initiator_id,
- receiverId = receiver_id,
- state = state,
- time = time,
- duration = duration,
- isVideo = video
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt
deleted file mode 100644
index a84b6e85..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkCurator.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkCurator
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkCurator(
- val id: Int,
- val name: String,
- val description: String,
- val url: String,
- val photo: List
-) : BaseVkAttachment() {
-
- fun asVkCurator() = VkCurator(
- id = id
- )
-
- @Parcelize
- data class Photo(
- val height: Int,
- val url: String,
- val width: String
- ) : Parcelable
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt
deleted file mode 100644
index a1b09ce8..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkEvent.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import com.meloda.fast.api.model.attachments.VkEvent
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkEvent(
- val button_text: String,
- val id: Int,
- val is_favorite: Boolean,
- val text: String,
- val address: String,
- val friends: List = emptyList(),
- val member_status: Int,
- val time: Int
-) : BaseVkAttachment() {
-
- fun asVkEvent() = VkEvent(id = id)
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
deleted file mode 100644
index edc8b96d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkFile.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkFile
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkFile(
- val id: Int,
- val owner_id: Int,
- val title: String,
- val size: Int,
- val ext: String,
- val date: Int,
- val type: Int,
- val url: String,
- val preview: Preview?,
- val ic_licensed: Int,
- val access_key: String?,
- val web_preview_url: String?
-) : BaseVkAttachment() {
-
- fun asVkFile() = VkFile(
- id = id,
- ownerId = owner_id,
- title = title,
- ext = ext,
- url = url,
- size = size,
- accessKey = access_key,
- preview = preview
- )
-
- @Parcelize
- data class Preview(
- val photo: Photo?,
- val video: Video?
- ) : Parcelable {
-
- @Parcelize
- data class Photo(val sizes: List) : Parcelable {
-
- @Parcelize
- data class Size(
- val height: Int,
- val width: Int,
- val type: String,
- val src: String
- ) : Parcelable
-
- }
-
- @Parcelize
- data class Video(
- val src: String,
- val width: Int,
- val height: Int,
- val file_size: Int
- ) : Parcelable
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
deleted file mode 100644
index 29e646b9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGift.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkGift
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkGift(
- val id: Int,
- val thumb_256: String?,
- val thumb_96: String?,
- val thumb_48: String
-) : Parcelable {
-
- fun asVkGift() = VkGift(
- id = id,
- thumb256 = thumb_256,
- thumb96 = thumb_96,
- thumb48 = thumb_48
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
deleted file mode 100644
index c5e841ef..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGraffiti.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkGraffiti
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkGraffiti(
- val id: Int,
- val owner_id: Int,
- val url: String,
- val width: Int,
- val height: Int,
- val access_key: String
-) : Parcelable {
-
- fun asVkGraffiti() = VkGraffiti(
- id = id,
- ownerId = owner_id,
- url = url,
- width = width,
- height = height,
- accessKey = access_key
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
deleted file mode 100644
index e9ff17c0..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkGroupCall.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkGroupCall
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkGroupCall(
- val initiator_id: Int,
- val join_link: String,
- val participants: Participants
-) : Parcelable {
-
- @Parcelize
- data class Participants(
- val list: List,
- val count: Int
- ) : Parcelable
-
- fun asVkGroupCall() = VkGroupCall(initiatorId = initiator_id)
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
deleted file mode 100644
index 4f1b59c5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkLink.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import com.meloda.fast.api.model.attachments.VkLink
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkLink(
- val url: String,
- val title: String?,
- val caption: String?,
- val photo: BaseVkPhoto?,
- val target: String?,
- val is_favorite: Boolean
-) : BaseVkAttachment() {
-
- fun asVkLink() = VkLink(
- url = url,
- title = title,
- caption = caption,
- photo = photo?.asVkPhoto(),
- target = target,
- isFavorite = is_favorite
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
deleted file mode 100644
index 8e857850..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkMiniApp.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import com.meloda.fast.api.model.attachments.VkMiniApp
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkMiniApp(
- val title: String,
- val description: String,
- val app: App,
- val images: List?,
- val button_text: String
-) : Parcelable {
-
- @Parcelize
- data class App(
- val type: String,
- val id: Int,
- val title: String,
- @SerializedName("author_owner_id")
- val authorOwnerId: Int,
- @SerializedName("are_notifications_enabled")
- val areNotificationsEnabled: Boolean,
- @SerializedName("is_favorite")
- val isFavorite: Boolean,
- @SerializedName("is_installed")
- val isInstalled: Boolean,
- @SerializedName("track_code")
- val trackCode: String,
- @SerializedName("share_url")
- val shareUrl: String,
- @SerializedName("webview_url")
- val webViewUrl: String,
- @SerializedName("hide_tabbar")
- val hideTabBar: Int,
- @SerializedName("icon_75")
- val icon75: String?,
- @SerializedName("icon_139")
- val icon139: String?,
- @SerializedName("icon_150")
- val icon150: String?,
- @SerializedName("icon_278")
- val icon278: String?,
- @SerializedName("icon_576")
- val icon576: String?,
- @SerializedName("open_in_external_browser")
- val openInExternalBrowser: Boolean,
- @SerializedName("need_policy_confirmation")
- val needPolicyConfirmation: Boolean,
- @SerializedName("is_vkui_internal")
- val isVkUiInternal: Boolean,
- @SerializedName("has_vk_connect")
- val hasVkConnect: Boolean,
- @SerializedName("need_show_bottom_menu_tooltip_on_close")
- val needShowBottomMenuTooltipOnClose: Boolean
- ) : Parcelable
-
- @Parcelize
- data class Image(
- val height: Int,
- val width: Int,
- val url: String
- ) : Parcelable
-
- fun asVkMiniApp() = VkMiniApp(link = app.shareUrl)
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
deleted file mode 100644
index babe40e4..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPhoto.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkPhoto
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkPhoto(
- val album_id: Int,
- val date: Int,
- val id: Int,
- val owner_id: Int,
- val has_tags: Boolean,
- val access_key: String?,
- val sizes: List,
- val text: String?,
- val user_id: Int?,
- val lat: Double?,
- val long: Double?,
- val post_id: Int?
-) : BaseVkAttachment() {
-
- fun asVkPhoto() = VkPhoto(
- albumId = album_id,
- date = date,
- id = id,
- ownerId = owner_id,
- hasTags = has_tags,
- accessKey = access_key,
- sizes = sizes,
- text = text,
- userId = user_id
- )
-
- @Parcelize
- data class Size(
- val height: Int,
- val width: Int,
- val type: String,
- val url: String
- ) : Parcelable
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
deleted file mode 100644
index 521145bb..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkPoll.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkPoll
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkPoll(
- val multiple: Boolean,
- val id: Int,
- val votes: Int,
- val anonymous: Boolean,
- val closed: Boolean,
- val end_date: Int,
- val is_board: Boolean,
- val can_vote: Boolean,
- val can_edit: Boolean,
- val can_report: Boolean,
- val can_share: Boolean,
- val created: Int,
- val owner_id: Int,
- val question: String,
- val disable_unvote: Boolean,
- val friends: List?,
- val embed_hash: String,
- val answers: List,
- val author_id: Int,
- val background: Background?
-) : Parcelable {
-
- @Parcelize
- data class Friend(
- val id: Int
- ) : Parcelable
-
- @Parcelize
- data class Answer(
- val id: Int,
- val rate: Double,
- val text: String,
- val votes: Int
- ) : Parcelable
-
- @Parcelize
- data class Background(
- val angle: Int,
- val color: String,
- val id: Int,
- val name: String,
- val type: String,
- val points: List
- ) : Parcelable {
-
- @Parcelize
- data class Point(
- val color: String,
- val position: Double
- ) : Parcelable
- }
-
- fun asVkPoll() = VkPoll(id = id)
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
deleted file mode 100644
index 79e053d1..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkSticker.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkSticker
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkSticker(
- val product_id: Int,
- val sticker_id: Int,
- val images: List,
- val images_with_background: List,
- val animation_url: String?,
- val animations: List?
-) : Parcelable {
-
- fun asVkSticker() = VkSticker(
- id = sticker_id,
- productId = product_id,
- images = images,
- backgroundImages = images_with_background
- )
-
- @Parcelize
- data class Image(
- val width: Int,
- val height: Int,
- val url: String
- ) : Parcelable
-
- @Parcelize
- data class Animation(
- val type: String,
- val url: String
- ) : Parcelable
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt
deleted file mode 100644
index d16f2d51..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkStory.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkStory
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkStory(
- val id: Int,
- val owner_id: Int,
- val access_key: String,
- val can_comment: Int,
- val can_reply: Int,
- val can_like: Boolean,
- val can_share: Int,
- val can_hide: Int,
- val date: Int,
- val expires_at: Int,
- val is_ads: Boolean,
- val photo: BaseVkPhoto?,
- val replies: Replies,
- val is_one_time: Boolean,
- val track_code: String,
- val type: String,
- val views: Int,
- val likes_count: Int,
- val reaction_set_id: String,
- val is_restricted: Boolean,
- val no_sound: Boolean,
- val need_mute: Boolean,
- val mute_reply: Boolean,
- val can_ask: Int,
- val can_ask_anonymous: Int,
- val preloading_enabled: Boolean,
- val narratives_count: Int,
- val can_use_in_narrative: Boolean
-) : BaseVkAttachment() {
-
- fun asVkStory() = VkStory(
- id = id,
- ownerId = owner_id,
- date = date,
- photo = photo?.asVkPhoto()
- )
-
- @Parcelize
- data class Replies(
- val count: Int,
- val new: Int
- ) : Parcelable
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
deleted file mode 100644
index 0cce54de..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVideo.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkVideo
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkVideo(
- val id: Int,
- val title: String,
- val width: Int,
- val height: Int,
- val duration: Int,
- val date: Int,
- val comments: Int,
- val description: String,
- val player: String,
- val added: Int,
- val type: String,
- val views: Int,
- val can_comment: Int,
- val can_edit: Int,
- val can_like: Int,
- val can_repost: Int,
- val can_subscribe: Int,
- val can_add_to_faves: Int,
- val can_add: Int,
- val can_attach_link: Int,
- val access_key: String?,
- val owner_id: Int,
- val ov_id: String,
- val is_favorite: Boolean,
- val track_code: String,
- val image: List,
- val first_frame: List,
- val files: File,
- val timeline_thumbs: TimelineThumbs,
- val ads: Ads
-) : BaseVkAttachment() {
-
- fun asVkVideo() = VkVideo(
- id = id,
- ownerId = owner_id,
- images = image.map { it.asVideoImage() },
- firstFrames = first_frame,
- accessKey = access_key,
- title = title
- )
-
- @Parcelize
- data class Image(
- val width: Int,
- val height: Int,
- val url: String,
- val with_padding: Int?
- ) : Parcelable {
-
- fun asVideoImage() = VkVideo.VideoImage(
- width = width,
- height = height,
- url = url,
- withPadding = with_padding == 1
- )
- }
-
- @Parcelize
- data class FirstFrame(
- val height: Int,
- val width: Int,
- val url: String
- ) : Parcelable
-
- @Parcelize
- data class File(
- val mp4_240: String?,
- val mp4_360: String?,
- val mp4_480: String?,
- val mp4_720: String?,
- val mp4_1080: String?,
- val mp4_1440: String?,
- val hls: String,
- val dash_uni: String,
- val dash_sep: String,
- val hls_ondemand: String,
- val dash_ondemand: String,
- val failover_host: String
- ) : Parcelable
-
- @Parcelize
- data class TimelineThumbs(
- val count_per_image: Int,
- val count_per_row: Int,
- val count_total: Int,
- val frame_height: Int,
- val frame_width: Float,
- val links: List,
- val is_uv: Boolean,
- val frequency: Int
- ) : Parcelable
-
- @Parcelize
- data class Ads(
- val slot_id: Int,
- val timeout: Int,
- val can_play: Int,
- val params: Params,
- val sections: List,
- val midroll_percents: List
- ) : Parcelable {
-
- @Parcelize
- data class Params(
- val vk_id: Int,
- val duration: Int,
- val video_id: String,
- val pl: Int,
- val content_id: String,
- val lang: Int,
- val puid1: String,
- val puid2: Int,
- val puid3: Int,
- val puid5: Int,
- val puid6: Int,
- val puid7: Int,
- val puid9: Int,
- val puid10: Int,
- val puid12: Int,
- val puid13: Int,
- val puid14: Int,
- val puid15: Int,
- val puid18: Int,
- val puid21: Int,
- val sign: String,
- val groupId: Int,
- val vk_catid: Int,
- val is_xz_video: Int
- ) : Parcelable
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
deleted file mode 100644
index 23088e26..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkVoiceMessage.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkVoiceMessage
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkVoiceMessage(
- val id: Int,
- val owner_id: Int,
- val duration: Int,
- val waveform: List,
- val link_ogg: String,
- val link_mp3: String,
- val access_key: String,
- val transcript_state: String?,
- val transcript: String?
-) : Parcelable {
-
- fun asVkVoiceMessage() = VkVoiceMessage(
- id = id,
- ownerId = owner_id,
- duration = duration,
- waveform = waveform,
- linkOgg = link_ogg,
- linkMp3 = link_mp3,
- accessKey = access_key,
- transcriptState = transcript_state,
- transcript = transcript
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
deleted file mode 100644
index e1dee465..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWall.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.attachments.VkWall
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkWall(
- val id: Int,
- val from_id: Int,
- val to_id: Int,
- val date: Int,
- val text: String,
- val attachments: List?,
- val post_source: PostSource?,
- val comments: Comments?,
- val likes: Likes?,
- val reposts: Reposts?,
- val views: Views?,
- val is_favorite: Boolean,
- val donut: Donut?,
- val access_key: String?,
- val short_text_rate: Double
-) : Parcelable {
-
- fun asVkWall() = VkWall(
- id = id,
- fromId = from_id,
- toId = to_id,
- date = date,
- text = text,
- attachments = attachments,
- comments = comments?.count,
- likes = likes?.count,
- reposts = reposts?.count,
- views = views?.count,
- isFavorite = is_favorite,
- accessKey = access_key
- )
-
- @Parcelize
- data class PostSource(
- val type: String,
- val platform: String
- ) : Parcelable
-
- @Parcelize
- data class Comments(
- val count: Int,
- val can_post: Int,
- val groups_can_post: Boolean
- ) : Parcelable
-
- @Parcelize
- data class Likes(
- val count: Int,
- val user_likes: Int,
- val can_like: Int,
- val can_publish: Int,
- ) : Parcelable
-
- @Parcelize
- data class Reposts(
- val count: Int,
- val user_reposted: Int
- ) : Parcelable
-
- @Parcelize
- data class Views(
- val count: Int
- ) : Parcelable
-
- @Parcelize
- data class Donut(
- val is_donut: Boolean
- ) : Parcelable
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWidget.kt b/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWidget.kt
deleted file mode 100644
index a6bf1006..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/base/attachments/BaseVkWidget.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.api.model.base.attachments
-
-import com.meloda.fast.api.model.attachments.VkWidget
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkWidget(val id: Int) : BaseVkAttachment() {
-
- fun asVkWidget() = VkWidget(id)
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/data/BaseVkConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/data/BaseVkConversation.kt
deleted file mode 100644
index 933f778b..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/data/BaseVkConversation.kt
+++ /dev/null
@@ -1,147 +0,0 @@
-package com.meloda.fast.api.model.data
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.model.base.BaseVkMessage
-import com.meloda.fast.api.model.base.attachments.BaseVkGroupCall
-import com.meloda.fast.api.model.domain.VkConversationDomain
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class BaseVkConversation(
- val peer: Peer,
- val last_message_id: Int,
- val in_read: Int,
- val out_read: Int,
- val in_read_cmid: Int,
- val out_read_cmid: Int,
- val sort_id: SortId,
- val last_conversation_message_id: Int,
- val is_marked_unread: Boolean,
- val important: Boolean,
- val push_settings: PushSettings,
- val can_write: CanWrite,
- val can_send_money: Boolean,
- val can_receive_money: Boolean,
- val chat_settings: ChatSettings?,
- val call_in_progress: CallInProgress?,
- val unread_count: Int?,
-) : Parcelable {
-
- @Parcelize
- data class Peer(
- val id: Int,
- val type: String,
- val local_id: Int,
- ) : Parcelable
-
- @Parcelize
- data class SortId(
- val major_id: Int,
- val minor_id: Int,
- ) : Parcelable
-
- @Parcelize
- data class PushSettings(
- val disabled_forever: Boolean,
- val no_sound: Boolean,
- val disabled_mentions: Boolean,
- val disabled_mass_mentions: Boolean,
- ) : Parcelable
-
- @Parcelize
- data class CanWrite(
- val allowed: Boolean,
- ) : Parcelable
-
- @Parcelize
- data class ChatSettings(
- val owner_id: Int,
- val title: String,
- val state: String,
- val acl: Acl,
- val members_count: Int,
- val friends_count: Int,
- val photo: Photo?,
- val admin_ids: List,
- val active_ids: List,
- val is_group_channel: Boolean,
- val is_disappearing: Boolean,
- val is_service: Boolean,
- val theme: String?,
- val pinned_message: BaseVkMessage?,
- ) : Parcelable {
-
- @Parcelize
- data class Acl(
- val can_change_info: Boolean,
- val can_change_invite_link: Boolean,
- val can_change_pin: Boolean,
- val can_invite: Boolean,
- val can_promote_users: Boolean,
- val can_see_invite_link: Boolean,
- val can_moderate: Boolean,
- val can_copy_chat: Boolean,
- val can_call: Boolean,
- val can_use_mass_mentions: Boolean,
- val can_change_style: Boolean,
- ) : Parcelable
-
- @Parcelize
- data class Photo(
- val photo_50: String?,
- val photo_100: String?,
- val photo_200: String?,
- val is_default_photo: Boolean,
- ) : Parcelable
- }
-
- @Parcelize
- data class CallInProgress(
- val participants: BaseVkGroupCall.Participants,
- val join_link: String,
- ) : Parcelable {
-
- @Parcelize
- data class Participants(
- val list: List,
- val count: Int,
- ) : Parcelable
-
- }
-
- fun mapToDomain(
- lastMessage: VkMessage? = null,
- conversationUser: VkUser? = null,
- conversationGroup: VkGroup? = null,
- ) = VkConversationDomain(
- id = peer.id,
- localId = peer.local_id,
- conversationTitle = chat_settings?.title,
- conversationPhoto = chat_settings?.photo?.photo_200,
- type = peer.type,
- isCallInProgress = call_in_progress != null,
- isPhantom = chat_settings?.is_disappearing == true,
- lastConversationMessageId = last_conversation_message_id,
- inRead = in_read,
- outRead = out_read,
- lastMessageId = last_message_id,
- unreadCount = unread_count ?: 0,
- membersCount = chat_settings?.members_count,
- ownerId = chat_settings?.owner_id,
- majorId = sort_id.major_id,
- minorId = sort_id.minor_id,
- canChangePin = chat_settings?.acl?.can_change_pin == true,
- canChangeInfo = chat_settings?.acl?.can_change_info == true,
- pinnedMessageId = chat_settings?.pinned_message?.id,
- inReadCmId = in_read_cmid,
- outReadCmId = out_read_cmid,
- ).also {
- it.lastMessage = lastMessage
- it.pinnedMessage = chat_settings?.pinned_message?.asVkMessage()
- it.conversationUser = conversationUser
- it.conversationGroup = conversationGroup
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/domain/VkConversationDomain.kt b/app/src/main/kotlin/com/meloda/fast/api/model/domain/VkConversationDomain.kt
deleted file mode 100644
index 89f4d731..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/domain/VkConversationDomain.kt
+++ /dev/null
@@ -1,245 +0,0 @@
-package com.meloda.fast.api.model.domain
-
-import android.os.Parcelable
-import androidx.room.Entity
-import androidx.room.Ignore
-import androidx.room.PrimaryKey
-import com.meloda.fast.R
-import com.meloda.fast.api.UserConfig
-import com.meloda.fast.api.VkUtils
-import com.meloda.fast.api.model.ActionState
-import com.meloda.fast.api.model.ConversationPeerType
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.model.presentation.VkConversationUi
-import com.meloda.fast.common.AppGlobal
-import com.meloda.fast.ext.isFalse
-import com.meloda.fast.ext.isTrue
-import com.meloda.fast.ext.orDots
-import com.meloda.fast.model.base.UiImage
-import com.meloda.fast.model.base.UiText
-import com.meloda.fast.model.base.parseString
-import com.meloda.fast.util.TimeUtils
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-import java.util.Calendar
-
-@Suppress("MemberVisibilityCanBePrivate")
-@Entity(tableName = "conversations")
-@Parcelize
-data class VkConversationDomain(
- @PrimaryKey(autoGenerate = false)
- val id: Int,
- val localId: Int,
- val ownerId: Int?,
- val conversationTitle: String?,
- val conversationPhoto: String?,
- val isCallInProgress: Boolean,
- val isPhantom: Boolean,
- val lastConversationMessageId: Int,
- val inReadCmId: Int,
- val outReadCmId: Int,
- val inRead: Int,
- val outRead: Int,
- val lastMessageId: Int,
- val unreadCount: Int,
- val membersCount: Int?,
- val canChangePin: Boolean,
- val canChangeInfo: Boolean,
- val majorId: Int,
- val minorId: Int,
- val pinnedMessageId: Int?,
- val type: String,
-) : Parcelable {
-
- @Ignore
- @IgnoredOnParcel
- var peerType: ConversationPeerType = ConversationPeerType.parse(type)
-
- @Ignore
- @IgnoredOnParcel
- var lastMessage: VkMessage? = null
-
- @Ignore
- @IgnoredOnParcel
- var pinnedMessage: VkMessage? = null
-
- @Ignore
- @IgnoredOnParcel
- var conversationUser: VkUser? = null
-
- @Ignore
- @IgnoredOnParcel
- var conversationGroup: VkGroup? = null
-
- fun isChat() = peerType.isChat()
- fun isUser() = peerType.isUser()
- fun isGroup() = peerType.isGroup()
-
- fun isInUnread() = inRead - lastMessageId < 0
- fun isOutUnread() = outRead - lastMessageId < 0
-
- fun isUnread() = isInUnread() || isOutUnread()
-
- fun isAccount() = id == UserConfig.userId
-
- fun isPinned() = majorId > 0
-
- fun extractAvatar(): UiImage {
- val placeholderImage = UiImage.Resource(R.drawable.ic_account_circle_cut)
-
- val avatarLink = when {
- peerType.isUser() -> {
- if (id == UserConfig.userId) {
- null
- } else {
- conversationUser?.photo200
- }
- }
-
- peerType.isGroup() -> conversationGroup?.photo200
- peerType.isChat() -> conversationPhoto
- else -> null
- }
-
- return avatarLink?.let(UiImage::Url) ?: placeholderImage
- }
-
- fun extractTitle(): UiText {
- return when {
- isAccount() -> UiText.Resource(R.string.favorites)
- peerType.isChat() -> UiText.Simple(conversationTitle ?: "...")
- peerType.isUser() -> UiText.Simple(conversationUser?.fullName ?: "...")
- peerType.isGroup() -> UiText.Simple(conversationGroup?.name ?: "...")
- else -> UiText.Simple("...")
- }
- }
-
- fun extractUnreadCounterText(): String? {
- if (lastMessage?.isOut.isFalse && !isInUnread()) return null
-
- return when (unreadCount) {
- in 1..999 -> unreadCount.toString()
- 0 -> null
- else -> "%dK".format(unreadCount / 1000)
- }
- }
-
- // TODO: 07.01.2023, Danil Nikolaev: rewrite
- fun extractMessage(): String {
- val actionMessage = VkUtils.getActionConversationText(
- message = lastMessage,
- youPrefix = "You",
- messageUser = lastMessage?.user,
- messageGroup = lastMessage?.group,
- action = lastMessage?.getPreparedAction(),
- actionUser = lastMessage?.actionUser,
- actionGroup = lastMessage?.actionGroup
- )
-
- val attachmentIcon: UiImage? = when {
- lastMessage?.text == null -> null
- !lastMessage?.forwards.isNullOrEmpty() -> {
- if (lastMessage?.forwards?.size == 1) {
- UiImage.Resource(R.drawable.ic_attachment_forwarded_message)
- } else {
- UiImage.Resource(R.drawable.ic_attachment_forwarded_messages)
- }
- }
-
- else -> VkUtils.getAttachmentConversationIcon(lastMessage)
- }
-
- val attachmentText = (if (attachmentIcon == null) VkUtils.getAttachmentText(
- message = lastMessage
- ) else null)
-
- val forwardsMessage = (if (lastMessage?.text == null) VkUtils.getForwardsText(
- message = lastMessage
- ) else null)
-
- val messageText = lastMessage?.text?.let(UiText::Simple)
-
- var prefix = when {
- actionMessage != null -> ""
- lastMessage?.isOut.isTrue -> "You: "
- else ->
- when {
- lastMessage?.user != null && lastMessage?.user?.firstName?.isNotBlank().isTrue -> {
- "${lastMessage?.user?.firstName}: "
- }
-
- lastMessage?.group != null && lastMessage?.group?.name?.isNotBlank().isTrue -> {
- "${lastMessage?.group?.name}: "
- }
-
- else -> ""
- }
- }
-
- if ((!peerType.isChat() && lastMessage?.isOut.isFalse) || id == UserConfig.userId)
- prefix = ""
-
- val finalText =
- (actionMessage ?: forwardsMessage ?: attachmentText ?: messageText)
- ?.parseString(AppGlobal.Instance)
- ?.let(VkUtils::prepareMessageText)
- ?.let { text -> "$prefix$text" }
-
-
- return finalText.orDots()
- }
-
- fun extractAttachmentImage(): UiImage? {
- if (lastMessage?.text == null) return null
- return VkUtils.getAttachmentConversationIcon(lastMessage)
- }
-
- fun extractReadCondition(): Boolean {
- return (lastMessage?.isOut.isTrue && isOutUnread()) ||
- (lastMessage?.isOut.isFalse && isInUnread())
- }
-
- fun extractDate(): String {
- return TimeUtils.getLocalizedTime(AppGlobal.Instance, (lastMessage?.date ?: -1) * 1000L)
- }
-
- // TODO: 05.08.2023, Danil Nikolaev: rewrite
- fun extractBirthday(): Boolean {
- val birthday = conversationUser?.birthday ?: return false
- val splitBirthday = birthday.split(".")
-
- return if (splitBirthday.size > 1) {
- val birthdayCalendar = Calendar.getInstance().apply {
- this[Calendar.DAY_OF_MONTH] = splitBirthday.first().toIntOrNull() ?: -1
- this[Calendar.MONTH] = (splitBirthday[1].toIntOrNull() ?: 0) - 1
- }
- val nowCalendar = Calendar.getInstance()
-
- (nowCalendar[Calendar.DAY_OF_MONTH] == birthdayCalendar[Calendar.DAY_OF_MONTH]
- && nowCalendar[Calendar.MONTH] == birthdayCalendar[Calendar.MONTH])
- } else false
- }
-
- fun mapToPresentation() = VkConversationUi(
- conversationId = id,
- lastMessageId = lastMessageId,
- avatar = extractAvatar(),
- title = extractTitle(),
- unreadCount = extractUnreadCounterText(),
- date = extractDate(),
- message = extractMessage(),
- attachmentImage = extractAttachmentImage(),
- isPinned = majorId > 0,
- actionState = ActionState.parse(isPhantom, isCallInProgress),
- isBirthday = extractBirthday(),
- isUnread = extractReadCondition(),
- isAccount = isAccount(),
- isOnline = !isAccount() && conversationUser?.online == true,
- lastMessage = lastMessage,
- conversationUser = conversationUser,
- conversationGroup = conversationGroup,
- peerType = peerType
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/presentation/VkConversationUi.kt b/app/src/main/kotlin/com/meloda/fast/api/model/presentation/VkConversationUi.kt
deleted file mode 100644
index 2f731153..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/model/presentation/VkConversationUi.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.meloda.fast.api.model.presentation
-
-import com.meloda.fast.api.model.ActionState
-import com.meloda.fast.api.model.ConversationPeerType
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.model.base.AdapterDiffItem
-import com.meloda.fast.model.base.UiImage
-import com.meloda.fast.model.base.UiText
-
-data class VkConversationUi(
- val conversationId: Int,
- val lastMessageId: Int,
- val avatar: UiImage,
- val title: UiText,
- val unreadCount: String?,
- val date: String,
- val message: String,
- val attachmentImage: UiImage?,
- val isPinned: Boolean,
- val actionState: ActionState,
- val isBirthday: Boolean,
- val isUnread: Boolean,
- val isAccount: Boolean,
- val isOnline: Boolean,
- val lastMessage: VkMessage?,
- val conversationUser: VkUser?,
- val conversationGroup: VkGroup?,
- val peerType: ConversationPeerType,
-) : AdapterDiffItem {
- override val id = conversationId
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/ApiErrors.kt b/app/src/main/kotlin/com/meloda/fast/api/network/ApiErrors.kt
deleted file mode 100644
index 057ebb6c..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/ApiErrors.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.meloda.fast.api.network
-
-import com.google.gson.annotations.SerializedName
-import com.meloda.fast.api.base.ApiError
-
-@Suppress("unused")
-object VkErrorCodes {
- const val UnknownError = 1
- const val AppDisabled = 2
- const val UnknownMethod = 3
- const val InvalidSignature = 4
- const val UserAuthorizationFailed = 5
- const val TooManyRequests = 6
- const val NoRights = 7
- const val BadRequest = 8
- const val TooManySimilarActions = 9
- const val InternalServerError = 10
- const val InTestMode = 11
- const val ExecuteCodeCompileError = 12
- const val ExecuteCodeRuntimeError = 13
- const val CaptchaNeeded = 14
- const val AccessDenied = 15
- const val RequiresRequestsOverHttps = 16
- const val ValidationRequired = 17
- const val UserBannedOrDeleted = 18
- const val ActionProhibited = 20
- const val ActionAllowedOnlyForStandalone = 21
- const val MethodOff = 23
- const val ConfirmationRequired = 24
- const val ParameterIsNotSpecified = 100
- const val IncorrectAppId = 101
- const val OutOfLimits = 103
- const val IncorrectUserId = 113
- const val IncorrectTimestamp = 150
- const val AccessToAlbumDenied = 200
- const val AccessToAudioDenied = 201
- const val AccessToGroupDenied = 203
- const val AlbumIsFull = 300
- const val ActionDenied = 500
- const val PermissionDenied = 600
- const val CannotSendMessageBlackList = 900
- const val CannotSendMessageGroup = 901
- const val InvalidDocId = 1150
- const val InvalidDocTitle = 1152
- const val AccessToDocDenied = 1153
-
- const val AccessTokenExpired = 1117
-}
-
-object VkErrors {
- const val Unknown = "unknown_error"
-
- const val NeedValidation = "need_validation"
- const val NeedCaptcha = "need_captcha"
- const val InvalidRequest = "invalid_request"
-
-}
-
-object VkErrorTypes {
- const val OtpFormatIncorrect = "otp_format_is_incorrect"
- const val WrongOtp = "wrong_otp"
-}
-
-object VkErrorMessages {
- const val UserBanned = "user has been banned"
-}
-
-open class AuthorizationError : ApiError()
-
-class TokenExpiredError : AuthorizationError()
-
-data class ValidationRequiredError(
- @SerializedName("validation_type")
- val validationType: String,
- @SerializedName("validation_sid")
- val validationSid: String,
- @SerializedName("phone_mask")
- val phoneMask: String,
- @SerializedName("redirect_uri")
- val redirectUri: String,
- @SerializedName("validation_resend")
- val validationResend: String
-) : ApiError()
-
-data class CaptchaRequiredError(
- @SerializedName("captcha_sid")
- val captchaSid: String,
- @SerializedName("captcha_img")
- val captchaImg: String
-) : ApiError()
-
-object WrongTwoFaCodeFormatError : ApiError()
-
-object WrongTwoFaCodeError : ApiError()
-
-data class UserBannedError(
- @SerializedName("ban_info")
- val banInfo: BanInfo
-) : ApiError() {
-
- data class BanInfo(
- @SerializedName("member_name")
- val memberName: String,
- val message: String,
- @SerializedName("access_token")
- val accessToken: String,
- @SerializedName("restore_url")
- val restoreUrl: String
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt b/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt
deleted file mode 100644
index 32c4ca2f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/AuthInterceptor.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.meloda.fast.api.network
-
-import com.meloda.fast.api.UserConfig
-import com.meloda.fast.api.VKConstants
-import com.meloda.fast.api.network.account.AccountUrls
-import com.meloda.fast.api.network.ota.OtaUrls
-import okhttp3.Interceptor
-import okhttp3.Response
-import java.net.URLEncoder
-
-class AuthInterceptor : Interceptor {
-
- override fun intercept(chain: Interceptor.Chain): Response {
- val builder = chain.request().url.newBuilder()
-
- val url = builder.build().toUrl().toString()
-
- if (!url.contains("upload.php") && !url.contains(OtaUrls.GetActualUrl)) {
- builder.addQueryParameter("v", URLEncoder.encode(VKConstants.API_VERSION, "utf-8"))
- }
-
- if (!url.contains(AccountUrls.SetOnline) && !url.contains("upload.php")) {
- UserConfig.accessToken.let {
- if (it.isNotBlank())
- builder.addQueryParameter("access_token", URLEncoder.encode(it, "utf-8"))
- }
- }
-
- return chain.proceed(chain.request().newBuilder().apply { url(builder.build()) }.build())
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt b/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt
deleted file mode 100644
index 2cde43e2..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/ResultCallFactory.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-@file:Suppress("UNCHECKED_CAST")
-
-package com.meloda.fast.api.network
-
-import com.google.gson.Gson
-import com.meloda.fast.api.VkUtils
-import com.meloda.fast.api.base.ApiError
-import com.meloda.fast.api.base.ApiResponse
-import okhttp3.Request
-import okio.Timeout
-import retrofit2.*
-import java.lang.reflect.ParameterizedType
-import java.lang.reflect.Type
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.contract
-
-class ResultCallFactory(private val gson: Gson) : CallAdapter.Factory() {
- override fun get(
- returnType: Type,
- annotations: Array,
- retrofit: Retrofit,
- ): CallAdapter<*, *>? {
- val rawReturnType: Class<*> = getRawType(returnType)
- if (rawReturnType == Call::class.java) {
- if (returnType is ParameterizedType) {
- val callInnerType: Type = getParameterUpperBound(0, returnType)
- if (getRawType(callInnerType) == ApiAnswer::class.java) {
- if (callInnerType is ParameterizedType) {
- val resultInnerType = getParameterUpperBound(0, callInnerType)
- return ResultCallAdapter(resultInnerType, gson)
- }
- return ResultCallAdapter(Nothing::class.java, gson)
- }
- }
- }
- return null
- }
-}
-
-internal abstract class CallDelegate(protected val proxy: Call) : Call {
-
- override fun execute(): Response = throw NotImplementedError()
-
- final override fun enqueue(callback: Callback) = enqueueImpl(callback)
-
- final override fun clone(): Call = cloneImpl()
-
- override fun cancel() = proxy.cancel()
-
- override fun request(): Request = proxy.request()
-
- override fun isExecuted() = proxy.isExecuted
-
- override fun isCanceled() = proxy.isCanceled
-
- abstract fun enqueueImpl(callback: Callback)
-
- abstract fun cloneImpl(): Call
-}
-
-private class ResultCallAdapter(private val type: Type, private val gson: Gson) : CallAdapter>> {
-
- override fun responseType() = type
-
- override fun adapt(call: Call): Call> = ResultCall(call, gson)
-}
-
-internal class ResultCall(proxy: Call, private val gson: Gson) : CallDelegate>(proxy) {
-
- override fun enqueueImpl(callback: Callback>) {
- proxy.enqueue(ResultCallback(this, callback, gson))
- }
-
- override fun cloneImpl(): ResultCall {
- return ResultCall(proxy.clone(), gson)
- }
-
- private class ResultCallback(
- private val proxy: ResultCall,
- private val callback: Callback>,
- private val gson: Gson
- ) : Callback {
-
- override fun onResponse(call: Call, response: Response) {
- val result: ApiAnswer =
- if (response.isSuccessful) {
- val baseBody = response.body()
- if (baseBody !is ApiResponse<*>) {
- ApiAnswer.Success(baseBody as T)
- } else {
- val body = baseBody as? ApiResponse<*>
- if (body?.error != null) {
- VkUtils.getApiError(gson, gson.toJson(body.error))
- } else {
- ApiAnswer.Success(body as T)
- }
- }
- } else {
- val errorBodyString = response.errorBody()?.string()
-
- VkUtils.getApiError(gson, errorBodyString)
- }
-
- if (checkErrors(call, result)) {
- return
- }
-
- callback.onResponse(proxy, Response.success(result))
- }
-
- override fun onFailure(call: Call, error: Throwable) {
- callback.onResponse(
- proxy,
- Response.success(ApiAnswer.Error(ApiError(throwable = error)))
- )
- }
-
- private fun checkErrors(call: Call, result: ApiAnswer<*>): Boolean {
- if (result.isError()) {
- result.error.throwable?.run {
- onFailure(call, this)
- return true
- }
- }
-
- return false
- }
- }
-
- override fun timeout(): Timeout {
- return proxy.timeout()
- }
-}
-
-sealed class ApiAnswer {
-
- data class Success(val data: T) : ApiAnswer()
- data class Error(val error: ApiError) : ApiAnswer()
-
- @OptIn(ExperimentalContracts::class)
- fun isSuccessful(): Boolean {
- contract {
- returns(true) implies (this@ApiAnswer is Success)
- }
- return this is Success
- }
-
- @OptIn(ExperimentalContracts::class)
- fun isError(): Boolean {
- contract {
- returns(true) implies (this@ApiAnswer is Error)
- }
- return this is Error
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt
deleted file mode 100644
index a38e540e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/VkUrls.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.api.network
-
-object VkUrls {
-
- const val OAUTH = "https://oauth.vk.com"
- const val API = "https://api.vk.com/method"
-}
-
-
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/account/AccountUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/account/AccountUrls.kt
deleted file mode 100644
index c3588a2c..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/account/AccountUrls.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.meloda.fast.api.network.account
-
-import com.meloda.fast.api.network.VkUrls
-
-object AccountUrls {
-
- const val SetOnline = "${VkUrls.API}/account.setOnline"
- const val SetOffline = "${VkUrls.API}/account.setOffline"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosRequests.kt b/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosRequests.kt
deleted file mode 100644
index dfa5fbf3..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosRequests.kt
+++ /dev/null
@@ -1,2 +0,0 @@
-package com.meloda.fast.api.network.audio
-
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosResponses.kt b/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosResponses.kt
deleted file mode 100644
index a0a39180..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosResponses.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.meloda.fast.api.network.audio
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AudiosGetUploadServerResponse(
- @SerializedName("upload_url")
- val uploadUrl: String
-) : Parcelable
-
-@Parcelize
-data class AudiosUploadResponse(
- val redirect: String,
- val server: Int,
- val audio: String?,
- val hash: String,
- val error: String?
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosUrls.kt
deleted file mode 100644
index 094f32fa..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/audio/AudiosUrls.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.api.network.audio
-
-import com.meloda.fast.api.network.VkUrls
-
-object AudiosUrls {
-
- const val GetUploadServer = "${VkUrls.API}/audio.getUploadServer"
-
- const val Save = "${VkUrls.API}/audio.save"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthResponse.kt
deleted file mode 100644
index f206f415..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthResponse.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.network.auth
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AuthDirectResponse(
- @SerializedName("access_token") val accessToken: String?,
- @SerializedName("user_id") val userId: Int?,
- @SerializedName("trusted_hash") val twoFaHash: String?,
- @SerializedName("validation_sid") val validationSid: String?,
- @SerializedName("validation_type") val validationType: String?,
- @SerializedName("phone_mask") val phoneMask: String?,
- @SerializedName("redirect_uri") val redirectUrl: String?,
- @SerializedName("validation_resend") val validationResend: String?,
- @SerializedName("cant_get_code_open_restore") val isCanNotGetCodeNeedToOpenRestore: Boolean
-) : Parcelable
-
-@Parcelize
-data class SendSmsResponse(
- @SerializedName("sid") val validationSid: String?,
- @SerializedName("delay") val delay: Int?,
- @SerializedName("validation_type") val validationType: String?,
- @SerializedName("validation_resend") val validationResend: String?
-) : Parcelable
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthUrls.kt
deleted file mode 100644
index 1a888435..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/auth/AuthUrls.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.meloda.fast.api.network.auth
-
-import com.meloda.fast.api.network.VkUrls
-
-object AuthUrls {
-
- const val DirectAuth = "${VkUrls.OAUTH}/token"
- const val SendSms = "${VkUrls.API}/auth.validatePhone"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsResponse.kt
deleted file mode 100644
index c8f461a4..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsResponse.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.api.network.conversations
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import com.meloda.fast.api.model.data.BaseVkConversation
-import com.meloda.fast.api.model.base.BaseVkGroup
-import com.meloda.fast.api.model.base.BaseVkMessage
-import com.meloda.fast.api.model.base.BaseVkUser
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class ConversationsGetResponse(
- val count: Int,
- val items: List,
- @SerializedName("unread_count")
- val unreadCount: Int?,
- val profiles: List?,
- val groups: List?
-) : Parcelable
-
-@Parcelize
-data class ConversationsResponseItems(
- val conversation: BaseVkConversation,
- @SerializedName("last_message")
- val lastMessage: BaseVkMessage?
-) : Parcelable
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsUrls.kt
deleted file mode 100644
index 17715d2f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/conversations/ConversationsUrls.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.meloda.fast.api.network.conversations
-
-import com.meloda.fast.api.network.VkUrls
-
-object ConversationsUrls {
-
- const val Get = "${VkUrls.API}/messages.getConversations"
- const val Delete = "${VkUrls.API}/messages.deleteConversation"
- const val Pin = "${VkUrls.API}/messages.pinConversation"
- const val Unpin = "${VkUrls.API}/messages.unpinConversation"
- const val ReorderPinned = "${VkUrls.API}/messages.reorderPinnedConversations"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/files/FileRequests.kt b/app/src/main/kotlin/com/meloda/fast/api/network/files/FileRequests.kt
deleted file mode 100644
index a94949af..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/files/FileRequests.kt
+++ /dev/null
@@ -1,2 +0,0 @@
-package com.meloda.fast.api.network.files
-
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesResponses.kt b/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesResponses.kt
deleted file mode 100644
index abef98ae..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesResponses.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.meloda.fast.api.network.files
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import com.meloda.fast.api.model.base.attachments.BaseVkFile
-import com.meloda.fast.api.model.base.attachments.BaseVkVoiceMessage
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class FilesGetMessagesUploadServerResponse(
- @SerializedName("upload_url")
- val uploadUrl: String
-) : Parcelable
-
-@Parcelize
-data class FilesUploadFileResponse(val file: String?, val error: String?) : Parcelable
-
-@Parcelize
-data class FilesSaveFileResponse(
- val type: String,
- @SerializedName("doc")
- val file: BaseVkFile?,
- @SerializedName("audio_message")
- val voiceMessage: BaseVkVoiceMessage?
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesUrls.kt
deleted file mode 100644
index 1282e234..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/files/FilesUrls.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.api.network.files
-
-import com.meloda.fast.api.network.VkUrls
-
-object FilesUrls {
-
- const val GetMessagesUploadServer = "${VkUrls.API}/docs.getMessagesUploadServer"
-
- const val Save = "${VkUrls.API}/docs.save"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesResponse.kt
deleted file mode 100644
index 86dd9a6a..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesResponse.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.meloda.fast.api.network.messages
-
-import android.os.Parcelable
-import com.meloda.fast.api.model.base.*
-import com.meloda.fast.api.model.data.BaseVkConversation
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class MessagesGetHistoryResponse(
- val count: Int,
- val items: List = emptyList(),
- val conversations: List?,
- val profiles: List?,
- val groups: List?
-) : Parcelable
-
-@Parcelize
-data class MessagesGetByIdResponse(
- val count: Int,
- val items: List = emptyList(),
- val profiles: List?,
- val groups: List?
-) : Parcelable
-
-@Parcelize
-data class MessagesGetConversationMembersResponse(
- val count: Int,
- val items: List = emptyList(),
- val profiles: List?,
- val groups: List?
-) : Parcelable
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesUrls.kt
deleted file mode 100644
index 829c76c6..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/messages/MessagesUrls.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.meloda.fast.api.network.messages
-
-import com.meloda.fast.api.network.VkUrls
-
-object MessagesUrls {
-
- const val GetHistory = "${VkUrls.API}/messages.getHistory"
- const val Send = "${VkUrls.API}/messages.send"
- const val MarkAsImportant = "${VkUrls.API}/messages.markAsImportant"
- const val GetLongPollServer = "${VkUrls.API}/messages.getLongPollServer"
- const val GetLongPollHistory = "${VkUrls.API}/messages.getLongPollHistory"
- const val Pin = "${VkUrls.API}/messages.pin"
- const val Unpin = "${VkUrls.API}/messages.unpin"
- const val Delete = "${VkUrls.API}/messages.delete"
- const val Edit = "${VkUrls.API}/messages.edit"
- const val GetById = "${VkUrls.API}/messages.getById"
- const val MarkAsRead = "${VkUrls.API}/messages.markAsRead"
- const val GetChat = "${VkUrls.API}/messages.getChat"
- const val GetConversationMembers = "${VkUrls.API}/messages.getConversationMembers"
- const val RemoveChatUser = "${VkUrls.API}/messages.removeChatUser"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaResponses.kt b/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaResponses.kt
deleted file mode 100644
index fd0c6961..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaResponses.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.meloda.fast.api.network.ota
-
-import android.os.Parcelable
-import com.meloda.fast.model.UpdateItem
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class OtaGetLatestReleaseResponse(val release: UpdateItem?) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaUrls.kt
deleted file mode 100644
index 473d0ea9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/ota/OtaUrls.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.meloda.fast.api.network.ota
-
-object OtaUrls {
-
- const val GetActualUrl =
- "https://raw.githubusercontent.com/melod1n/ota-server/master/ngrok_url.json"
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotoUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotoUrls.kt
deleted file mode 100644
index 05b897e5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotoUrls.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.api.network.photos
-
-import com.meloda.fast.api.network.VkUrls
-
-object PhotoUrls {
-
- const val GetMessagesUploadServer = "${VkUrls.API}/photos.getMessagesUploadServer"
-
- const val SaveMessagePhoto = "${VkUrls.API}/photos.saveMessagesPhoto"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosRequests.kt b/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosRequests.kt
deleted file mode 100644
index 8e457ff0..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosRequests.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.meloda.fast.api.network.photos
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class PhotosSaveMessagePhotoRequest(
- val photo: String, val server: Int, val hash: String
-) : Parcelable {
- val map
- get() = mapOf(
- "photo" to photo,
- "server" to server.toString(),
- "hash" to hash
- )
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosResponses.kt b/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosResponses.kt
deleted file mode 100644
index c9a31973..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/photos/PhotosResponses.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.api.network.photos
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class PhotosGetMessagesUploadServerResponse(
- @SerializedName("album_id")
- val albumId: Int,
- @SerializedName("upload_url")
- val uploadUrl: String
-) : Parcelable
-
-@Parcelize
-data class PhotosUploadPhotoResponse(
- val server: Int, val photo: String, val hash: String
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersResponse.kt
deleted file mode 100644
index c4084e58..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersResponse.kt
+++ /dev/null
@@ -1,2 +0,0 @@
-package com.meloda.fast.api.network.users
-
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersUrls.kt
deleted file mode 100644
index 64761573..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/users/UsersUrls.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.api.network.users
-
-import com.meloda.fast.api.network.VkUrls
-
-object UsersUrls {
-
- const val GetById = "${VkUrls.API}/users.get"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosRequests.kt b/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosRequests.kt
deleted file mode 100644
index 1f196662..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosRequests.kt
+++ /dev/null
@@ -1,2 +0,0 @@
-package com.meloda.fast.api.network.videos
-
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosResponses.kt b/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosResponses.kt
deleted file mode 100644
index 86264318..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosResponses.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.meloda.fast.api.network.videos
-
-import android.os.Parcelable
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class VideosSaveResponse(
- @SerializedName("access_key")
- val accessKey: String,
- val description: String,
- @SerializedName("owner_id")
- val ownerId: Int,
- val title: String,
- @SerializedName("upload_url")
- val uploadUrl: String,
- @SerializedName("video_id")
- val videoId: Int
-) : Parcelable {
-
-}
-
-@Parcelize
-data class VideosUploadResponse(
- @SerializedName("video_hash")
- val hash: String?,
- val size: Int,
- @SerializedName("direct_link")
- val directLink: String,
- @SerializedName("owner_id")
- val ownerId: Int,
- @SerializedName("video_id")
- val videoId: Int,
- val error: String?
-) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosUrls.kt
deleted file mode 100644
index c2cc9308..00000000
--- a/app/src/main/kotlin/com/meloda/fast/api/network/videos/VideosUrls.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.api.network.videos
-
-import com.meloda.fast.api.network.VkUrls
-
-object VideosUrls {
-
- const val Save = "${VkUrls.API}/video.save"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt
deleted file mode 100644
index 3113c485..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.base
-
-import androidx.annotation.LayoutRes
-import androidx.appcompat.app.AppCompatActivity
-
-abstract class BaseActivity : AppCompatActivity {
-
- constructor() : super()
-
- constructor(@LayoutRes resId: Int) : super(resId)
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt
deleted file mode 100644
index 20d217db..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.meloda.fast.base
-
-import androidx.annotation.LayoutRes
-import androidx.fragment.app.Fragment
-
-abstract class BaseFragment : Fragment {
-
- constructor() : super()
-
- constructor(@LayoutRes resId: Int) : super(resId)
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/ResourceProvider.kt b/app/src/main/kotlin/com/meloda/fast/base/ResourceProvider.kt
deleted file mode 100644
index f2ab5f99..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/ResourceProvider.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.meloda.fast.base
-
-import android.content.Context
-import android.graphics.drawable.Drawable
-import androidx.annotation.ColorInt
-import androidx.annotation.ColorRes
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-import androidx.core.content.ContextCompat
-
-abstract class ResourceProvider(protected val context: Context) {
-
- protected fun getString(@StringRes resId: Int): String {
- return context.getString(resId)
- }
-
- @ColorInt
- protected fun getColor(@ColorRes resId: Int): Int {
- return ContextCompat.getColor(context, resId)
- }
-
- protected fun getDrawable(@DrawableRes resId: Int): Drawable? {
- return ContextCompat.getDrawable(context, resId)
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/AsyncDiffItemAdapter.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/AsyncDiffItemAdapter.kt
deleted file mode 100644
index 9167b253..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/AsyncDiffItemAdapter.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.meloda.fast.base.adapter
-
-import androidx.recyclerview.widget.DiffUtil
-import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
-import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
-import com.meloda.fast.model.base.AdapterDiffItem
-
-class AsyncDiffItemAdapter(
- customDiffCallback: DiffUtil.ItemCallback? = null,
- vararg delegates: AdapterDelegate>,
-) : AsyncListDifferDelegationAdapter(customDiffCallback ?: DIFF_CALLBACK) {
-
- constructor(
- vararg delegates: AdapterDelegate>,
- ) : this(customDiffCallback = null) {
- delegates.forEach(::addDelegate)
- }
-
- init {
- delegates.forEach(::addDelegate)
- }
-
- fun addDelegates(vararg delegates: AdapterDelegate>) {
- delegates.forEach(::addDelegate)
- }
-
- @Suppress("UNCHECKED_CAST")
- fun addDelegate(delegate: AdapterDelegate>) {
- (delegate as? AdapterDelegate>)?.let(delegatesManager::addDelegate)
- }
-
- fun isEmpty() = itemCount == 0
- fun isNotEmpty() = itemCount > 0
-
- companion object {
- val DIFF_CALLBACK = object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: AdapterDiffItem,
- newItem: AdapterDiffItem,
- ): Boolean {
- return oldItem.areItemsTheSame(newItem)
- }
-
- override fun areContentsTheSame(
- oldItem: AdapterDiffItem,
- newItem: AdapterDiffItem,
- ): Boolean {
- return oldItem.areContentsTheSame(newItem)
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt
deleted file mode 100644
index 22ac45b8..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt
+++ /dev/null
@@ -1,281 +0,0 @@
-package com.meloda.fast.base.adapter
-
-import android.content.Context
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.AdapterView
-import android.widget.Filter
-import android.widget.Filterable
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import kotlinx.coroutines.*
-import kotlin.properties.Delegates
-
-@Suppress("MemberVisibilityCanBePrivate", "unused", "UNCHECKED_CAST")
-abstract class BaseAdapter constructor(
- var context: Context,
- diffUtil: DiffUtil.ItemCallback,
- preAddedValues: List = emptyList(),
-) : ListAdapter(diffUtil), Filterable {
-
- private var valuesFilter: ValuesFilter? = null
-
- protected val adapterScope = CoroutineScope(Dispatchers.Default)
- private val cleanList = mutableListOf()
-
- protected var inflater: LayoutInflater = LayoutInflater.from(context)
-
- var itemClickListener: ((position: Int) -> Unit)? = null
- var itemLongClickListener: ((position: Int) -> Boolean)? = null
-
- private val listForSave = mutableListOf()
-
- var isSearching: Boolean by Delegates.observable(false) { _, _, _ ->
- updateSearchingState()
- }
-
- init {
- cleanList.addAll(preAddedValues)
- addAll(preAddedValues)
- }
-
- fun cloneCurrentList(): MutableList {
- return currentList.toMutableList()
- }
-
- open fun destroy() {}
-
- fun getOrNull(position: Int): T? {
- return if (position >= 0 && position <= currentList.lastIndex) get(position) else null
- }
-
- fun getOrElse(position: Int, defaultValue: (Int) -> T): T {
- return if (position >= 0 && position <= currentList.lastIndex) get(position)
- else defaultValue(position)
- }
-
- fun add(
- item: T,
- position: Int? = null,
- commitCallback: (() -> Unit)? = null
- ) = addAll(listOf(item), position, commitCallback)
-
- fun addAll(
- items: List,
- position: Int? = null,
- commitCallback: (() -> Unit)? = null
- ) {
- adapterScope.launch {
- val newList = cloneCurrentList()
- if (position == null) {
- val mutableItems = items.toMutableList()
-
- newList.addAll(mutableItems)
- cleanList.addAll(mutableItems)
- } else {
- newList.addAll(position, items)
- cleanList.addAll(position, items)
- }
-
- withContext(Dispatchers.Main) {
- submitList(newList, commitCallback)
- }
- }
- }
-
- fun remove(item: T, commitCallback: (() -> Unit)? = null) =
- removeAll(listOf(item), commitCallback)
-
- fun removeAll(items: List, commitCallback: (() -> Unit)? = null) {
- val newList = cloneCurrentList()
- newList.removeAll(items)
- cleanList.removeAll(items)
-
- submitList(newList, commitCallback)
- }
-
- fun removeAt(index: Int, commitCallback: (() -> Unit)? = null) {
- val newList = cloneCurrentList()
- newList.removeAt(index)
- cleanList.removeAt(index)
-
- submitList(newList, commitCallback)
- }
-
- fun clear(commitCallback: (() -> Unit)? = null) = removeAll(currentList, commitCallback)
-
- fun setItem(
- item: T,
- commitCallback: (() -> Unit)? = null
- ) = setItems(listOf(item), commitCallback)
-
- @Suppress("UNCHECKED_CAST")
- fun setItems(
- list: List?,
- commitCallback: (() -> Unit)? = null
- ) {
- adapterScope.launch {
- val items = mutableListOf()
- if (!list.isNullOrEmpty()) items.addAll(list)
-
- withContext(Dispatchers.Main) {
- if (items == currentList) {
- refreshList()
- } else {
- submitList(items, commitCallback)
- }
- }
- }
- }
-
- fun indexOf(item: T): Int {
- return currentList.indexOf(item)
- }
-
- fun searchIndexOf(item: T): Int? {
- val index = indexOf(item)
- return if (index == -1) null else index
- }
-
- val indices get() = currentList.indices
-
- operator fun get(position: Int): T {
- return currentList[position]
- }
-
- operator fun set(position: Int, item: T) = setItem(position, item)
-
- fun setItem(position: Int, item: T, commitCallback: (() -> Unit)? = null) {
- val newList = cloneCurrentList()
- newList[position] = item
- cleanList[position] = item
-
- submitList(newList, commitCallback)
- }
-
- fun isEmpty() = currentList.isEmpty()
- fun isNotEmpty() = currentList.isNotEmpty()
-
- fun refreshList() {
- notifyItemRangeChanged(0, itemCount)
- }
-
- fun updateCleanList(list: List?) {
- cleanList.clear()
- list?.run { cleanList.addAll(this) }
- }
-
- override fun submitList(list: List?) {
- super.submitList(list)
- updateCleanList(list)
- }
-
- override fun submitList(list: List?, commitCallback: Runnable?) {
- super.submitList(list, commitCallback)
- updateCleanList(list)
- }
-
- override fun onBindViewHolder(holder: VH, position: Int) {
- initListeners(holder.itemView, position)
- holder.bind(position)
- }
-
- protected open fun initListeners(itemView: View, position: Int) {
- if (itemView is AdapterView<*>) return
-
- itemView.setOnClickListener { itemClickListener?.invoke(position) }
- itemView.setOnLongClickListener {
- itemLongClickListener?.invoke(position)
- return@setOnLongClickListener itemClickListener != null
- }
- }
-
- override fun getItemCount(): Int {
- return currentList.size
- }
-
- val lastPosition get() = currentList.lastIndex
-
- private fun updateSearchingState() {
- Log.d("BaseAdapter", "updateSearchingState: $isSearching")
-
- cleanList.clear()
-
- if (isSearching) {
- listForSave.clear()
- listForSave += cloneCurrentList()
- } else {
- setItems(listForSave, commitCallback = {
- listForSave.clear()
- })
- }
- }
-
- open fun filter(query: String) {
- if (cleanList.isEmpty()) {
- cleanList.addAll(listForSave)
- }
-
- val newList = mutableListOf()
-
- setItems(emptyList(), commitCallback = {
- if (query.isEmpty()) {
- newList.addAll(cleanList)
- } else {
- for (item in cleanList) {
- if (onQueryItem(item, query)) {
- newList.add(item)
- }
- }
- }
-
- setItems(newList)
- })
- }
-
- open fun onQueryItem(item: T, query: String): Boolean {
- return false
- }
-
- override fun getFilter(): Filter {
- if (valuesFilter == null) {
- valuesFilter = ValuesFilter()
- }
-
- return requireNotNull(valuesFilter)
- }
-
- private inner class ValuesFilter : Filter() {
- override fun performFiltering(constraint: CharSequence?): FilterResults {
- val results = FilterResults()
-
- if (isEmpty()) return results
-
- if (!constraint.isNullOrEmpty()) {
- val filteredList = mutableListOf()
- for (item in listForSave) {
- if (onQueryItem(item, constraint.toString())) {
- filteredList.add(item)
- }
- }
- results.count = filteredList.size
- results.values = filteredList
- } else {
- results.count = listForSave.size
- results.values = listForSave
- }
-
- return results
- }
-
- override fun publishResults(constraint: CharSequence?, results: FilterResults) {
- val items = results.values as? List
- setItems(items)
- }
- }
-
- override fun onCurrentListChanged(previousList: MutableList, currentList: MutableList) {
- super.onCurrentListChanged(previousList, currentList)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt
deleted file mode 100644
index df2f0d63..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.meloda.fast.base.adapter
-
-import android.view.View
-import androidx.recyclerview.widget.RecyclerView
-
-abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) {
-
- open fun bind(position: Int) {
- bind(position, null)
- }
-
- open fun bind(position: Int, payloads: MutableList?) {}
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/Listeners.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/Listeners.kt
deleted file mode 100644
index 204ec669..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/adapter/Listeners.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.base.adapter
-
-fun interface OnItemClickListener {
- fun onItemClick(item: T)
-}
-
-fun interface OnItemLongClickListener {
- fun onLongItemClick(item: T): Boolean
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/screen/AppScreen.kt b/app/src/main/kotlin/com/meloda/fast/base/screen/AppScreen.kt
deleted file mode 100644
index 2fd269af..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/screen/AppScreen.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.meloda.fast.base.screen
-
-import com.github.terrakok.cicerone.Router
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
-
-interface AppScreen {
- val resultFlow: MutableSharedFlow
-
- var args: ArgType
-
- fun show(router: Router, args: ArgType)
-
- fun getArguments(): ArgType = args
-}
-
-@Suppress("unused")
-fun AppScreen.createResultFlow(): MutableSharedFlow {
- return MutableSharedFlow(
- extraBufferCapacity = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt
deleted file mode 100644
index 9509b8c4..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModel.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-import androidx.lifecycle.ViewModel
-import com.meloda.fast.api.base.ApiError
-import com.meloda.fast.api.network.*
-import com.meloda.fast.ext.isTrue
-import com.meloda.fast.ext.notNull
-
-abstract class BaseViewModel : ViewModel() {
-
- open suspend fun sendSingleEvent(event: VkEvent) {}
-
- suspend fun sendRequestNotNull(
- onError: ErrorHandler? = null,
- request: suspend () -> ApiAnswer
- ): T = sendRequest(onError, request).notNull()
-
- suspend fun sendRequest(
- onError: ErrorHandler? = null,
- request: suspend () -> ApiAnswer,
- ): T? {
- return when (val response = request()) {
- is ApiAnswer.Success -> response.data
- is ApiAnswer.Error -> {
- val error = response.error
-
- if (!onError?.handleError(error).isTrue) {
- checkErrors(error)
- }
-
- null
- }
- }
- }
-
- protected suspend fun checkErrors(throwable: Throwable) {
- when (throwable) {
- is TokenExpiredError -> {
- sendSingleEvent(TokenExpiredErrorEvent)
- }
- is AuthorizationError -> {
- sendSingleEvent(AuthorizationErrorEvent)
- }
- is UserBannedError -> {
- throwable.banInfo.let { banInfo ->
- sendSingleEvent(
- UserBannedEvent(
- memberName = banInfo.memberName,
- message = banInfo.message,
- restoreUrl = banInfo.restoreUrl,
- accessToken = banInfo.accessToken
- )
- )
- }
- }
- is ValidationRequiredError -> {
- sendSingleEvent(
- ValidationRequiredEvent(
- sid = throwable.validationSid,
- redirectUri = throwable.redirectUri,
- phoneMask = throwable.phoneMask,
- validationType = throwable.validationType,
- canResendSms = throwable.validationResend == "sms",
- codeError = null
- )
- )
- }
- is CaptchaRequiredError -> {
- sendSingleEvent(
- CaptchaRequiredEvent(
- sid = throwable.captchaSid,
- image = throwable.captchaImg
- )
- )
- }
-
- is ApiError -> {
- sendSingleEvent(
- if (throwable.errorMessage == null) {
- UnknownErrorEvent
- } else {
- ErrorTextEvent(errorText = requireNotNull(throwable.errorMessage))
- }
- )
- }
- else -> {
- sendSingleEvent(
- if (throwable.message == null) {
- UnknownErrorEvent
- } else {
- ErrorTextEvent(requireNotNull(throwable.message))
- }
- )
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModelFragment.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModelFragment.kt
deleted file mode 100644
index cb9f8b38..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseViewModelFragment.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.LayoutRes
-import androidx.lifecycle.lifecycleScope
-import com.meloda.fast.base.BaseFragment
-import kotlinx.coroutines.launch
-
-@Deprecated("", ReplaceWith("BaseFragment"))
-abstract class BaseViewModelFragment : BaseFragment {
-
- constructor() : super()
-
- constructor(@LayoutRes resId: Int) : super(resId)
-
- protected abstract val viewModel: VM
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- subscribeToViewModel(viewModel)
- }
-
- protected open fun onEvent(event: VkEvent) {
- ViewModelUtils.parseEvent(this, event)
- }
-
- protected fun subscribeToViewModel(viewModel: T) {
- lifecycleScope.launch {
- viewModel.tasksEvent.collect { onEvent(it) }
- }
- }
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/DeprecatedBaseViewModel.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/DeprecatedBaseViewModel.kt
deleted file mode 100644
index b88206ac..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/DeprecatedBaseViewModel.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.meloda.fast.api.base.ApiError
-import com.meloda.fast.api.network.*
-import com.meloda.fast.ext.isTrue
-import com.meloda.fast.ext.notNull
-import kotlinx.coroutines.CoroutineExceptionHandler
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.launch
-
-@Deprecated("rewrite")
-abstract class DeprecatedBaseViewModel : ViewModel() {
-
- private val tasksEventChannel = Channel()
- val tasksEvent = tasksEventChannel.receiveAsFlow()
-
- private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
- viewModelScope.launch { onException(throwable) }
- }
-
- fun launch(block: suspend CoroutineScope.() -> Unit): Job {
- return viewModelScope.launch(exceptionHandler, block = block)
- }
-
- suspend fun sendRequestNotNull(
- onError: ErrorHandler? = null,
- request: suspend () -> ApiAnswer
- ): T = sendRequest(onError, request).notNull()
-
- suspend fun sendRequest(
- onError: ErrorHandler? = null,
- request: suspend () -> ApiAnswer,
- ): T? {
- return when (val response = request()) {
- is ApiAnswer.Success -> response.data
- is ApiAnswer.Error -> {
- val error = response.error
-
- if (!onError?.handleError(error).isTrue) {
- checkErrors(error)
- }
-
- null
- }
- }
- }
-
- // TODO: 05.04.2023, Danil Nikolaev: переписать makeJob на sendRequest (oh boy, писать дохуя)
- // TODO: 05.04.2023, Danil Nikolaev: переписать Conversations Screen на новую архитектуру, пока что оставить View
-
- protected fun makeJob(
- job: suspend () -> ApiAnswer,
- onAnswer: suspend (T) -> Unit = {},
- onStart: (suspend () -> Unit)? = null,
- onEnd: (suspend () -> Unit)? = null,
- onError: (suspend (Throwable) -> Unit)? = null,
- onAnyResult: (suspend () -> Unit)? = null,
- ): Job = viewModelScope.launch {
- onStart?.invoke()
- when (val response = job()) {
- is ApiAnswer.Success -> {
- onAnswer(response.data)
- onAnyResult?.invoke()
- }
- is ApiAnswer.Error -> {
- onError?.invoke(response.error) ?: checkErrors(response.error)
- onAnyResult?.invoke()
- }
- }
- }.also {
- it.invokeOnCompletion {
- viewModelScope.launch {
- onEnd?.invoke()
- }
- }
- }
-
- protected open suspend fun onException(throwable: Throwable) {
- checkErrors(throwable)
- }
-
- protected suspend fun sendEvent(event: T) = tasksEventChannel.send(event)
-
- protected suspend fun checkErrors(throwable: Throwable) {
- when (throwable) {
- is TokenExpiredError -> sendEvent(TokenExpiredErrorEvent)
- is AuthorizationError -> sendEvent(AuthorizationErrorEvent)
- is UserBannedError -> {
- val banInfo = throwable.banInfo
- sendEvent(
- UserBannedEvent(
- memberName = banInfo.memberName,
- message = banInfo.message,
- restoreUrl = banInfo.restoreUrl,
- accessToken = banInfo.accessToken
- )
- )
- }
- is ValidationRequiredError -> {
- sendEvent(
- ValidationRequiredEvent(
- sid = throwable.validationSid,
- redirectUri = throwable.redirectUri,
- phoneMask = throwable.phoneMask,
- validationType = throwable.validationType,
- canResendSms = throwable.validationResend == "sms",
- codeError = null
- )
- )
- }
- is CaptchaRequiredError -> sendEvent(
- CaptchaRequiredEvent(
- sid = throwable.captchaSid,
- image = throwable.captchaImg
- )
- )
-
- is ApiError -> sendEvent(
- if (throwable.errorMessage == null) {
- UnknownErrorEvent
- } else {
- ErrorTextEvent(errorText = requireNotNull(throwable.errorMessage))
- }
- )
- else -> sendEvent(
- if (throwable.message == null) {
- UnknownErrorEvent
- } else {
- ErrorTextEvent(requireNotNull(throwable.message))
- }
- )
- }
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ErrorHandler.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ErrorHandler.kt
deleted file mode 100644
index aa702965..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ErrorHandler.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-fun interface ErrorHandler {
-
- /**
- * @return true if error has been handled manually
- */
- suspend fun handleError(error: Throwable): Boolean
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt
deleted file mode 100644
index 02acae53..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-import com.meloda.fast.model.base.UiText
-
-abstract class VkEvent
-
-abstract class VkErrorEvent(open val errorText: String? = null) : VkEvent()
-
-object UnknownErrorEvent : VkErrorEvent()
-open class ErrorTextEvent(override val errorText: String) : VkErrorEvent()
-
-object AuthorizationErrorEvent : VkErrorEvent()
-object TokenExpiredErrorEvent : VkErrorEvent()
-data class CaptchaRequiredEvent(val sid: String, val image: String) : VkErrorEvent()
-data class ValidationRequiredEvent(
- val sid: String,
- val redirectUri: String,
- val phoneMask: String,
- val validationType: String,
- val canResendSms: Boolean,
- val codeError: UiText?
-) : VkErrorEvent()
-
-data class UserBannedEvent(
- val memberName: String, val message: String, val restoreUrl: String, val accessToken: String,
-) : VkErrorEvent()
-
-fun interface VkEventCallback {
- fun onEvent(event: T)
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ViewModelUtils.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ViewModelUtils.kt
deleted file mode 100644
index 716c143f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/ViewModelUtils.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.meloda.fast.base.viewmodel
-
-import android.content.Intent
-import android.widget.Toast
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import com.meloda.fast.R
-import com.meloda.fast.api.UserConfig
-import com.meloda.fast.ext.showDialog
-import com.meloda.fast.model.base.UiText
-import com.meloda.fast.screens.main.activity.MainActivity
-
-object ViewModelUtils {
-
- @Deprecated("rewrite")
- @Suppress("MemberVisibilityCanBePrivate")
- fun parseEvent(activity: FragmentActivity, event: VkEvent) {
- when (event) {
- is AuthorizationErrorEvent -> {
- Toast.makeText(
- activity, R.string.authorization_failed, Toast.LENGTH_LONG
- ).show()
-
- UserConfig.clear()
- activity.finishAffinity()
- activity.startActivity(Intent(activity, MainActivity::class.java))
- }
- is TokenExpiredErrorEvent -> {
- Toast.makeText(
- activity, R.string.token_expired, Toast.LENGTH_LONG
- ).show()
-
- UserConfig.clear()
- activity.finishAffinity()
- activity.startActivity(Intent(activity, MainActivity::class.java))
- }
- is UserBannedEvent -> {
- // TODO: 17.04.2023, Danil Nikolaev: handle banned event
-// (activity as? MainActivity)?.accessRouter()?.newRootScreen(
-// Screens.UserBanned(
-// memberName = event.memberName,
-// message = event.message,
-// restoreUrl = event.restoreUrl,
-// accessToken = event.accessToken
-// )
-// )
- }
- is UnknownErrorEvent -> {
- activity.showDialog(
- title = UiText.Resource(R.string.title_error),
- message = UiText.Resource(R.string.unknown_error_occurred),
- positiveText = UiText.Resource(R.string.ok)
- )
- }
- is VkErrorEvent -> {
- event.errorText?.run {
- activity.showDialog(
- title = UiText.Resource(R.string.title_error),
- message = UiText.Simple(this),
- positiveText = UiText.Resource(R.string.ok)
- )
- }
- }
- }
- }
-
- @Deprecated("rewrite")
- fun parseEvent(fragment: Fragment, event: VkEvent) {
- parseEvent(fragment.requireActivity(), event)
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/common/AppConstants.kt b/app/src/main/kotlin/com/meloda/fast/common/AppConstants.kt
deleted file mode 100644
index 62d545a7..00000000
--- a/app/src/main/kotlin/com/meloda/fast/common/AppConstants.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.meloda.fast.common
-
-object AppConstants {
-
- const val INSTALL_APP_MIME_TYPE = "application/vnd.android.package-archive"
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
deleted file mode 100644
index e8c3b4f8..00000000
--- a/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.meloda.fast.common
-
-import android.app.Application
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.pm.PackageManager
-import android.content.res.Resources
-import android.media.AudioManager
-import androidx.appcompat.app.AppCompatDelegate
-import androidx.core.content.pm.PackageInfoCompat
-import androidx.preference.PreferenceManager
-import com.google.android.material.color.DynamicColors
-import com.meloda.fast.common.di.applicationModule
-import com.meloda.fast.screens.settings.SettingsFragment
-import com.meloda.fast.util.AndroidUtils
-import org.koin.android.ext.koin.androidContext
-import org.koin.android.ext.koin.androidLogger
-import org.koin.core.context.GlobalContext.startKoin
-import kotlin.math.roundToInt
-import kotlin.properties.Delegates
-
-class AppGlobal : Application() {
-
- override fun onCreate() {
- super.onCreate()
-
- instance = this
-
- if (preferences.getBoolean(
- SettingsFragment.KEY_USE_DYNAMIC_COLORS,
- SettingsFragment.DEFAULT_VALUE_USE_DYNAMIC_COLORS
- )
- ) {
- DynamicColors.applyToActivitiesIfAvailable(this)
- }
-
- val info = packageManager.getPackageInfo(this.packageName, PackageManager.GET_ACTIVITIES)
- versionName = info.versionName
- versionCode = PackageInfoCompat.getLongVersionCode(info).toInt()
-
- screenWidth80 = (AndroidUtils.getDisplayWidth() * 0.8).roundToInt()
-
- audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
-
- applyDarkTheme()
-
- initKoin()
- }
-
- private fun applyDarkTheme() {
- val nightMode = preferences.getInt(
- SettingsFragment.KEY_APPEARANCE_DARK_THEME,
- SettingsFragment.DEFAULT_VALUE_APPEARANCE_DARK_THEME
- )
- AppCompatDelegate.setDefaultNightMode(nightMode)
- }
-
- private fun initKoin() {
- startKoin {
- androidLogger()
- androidContext(this@AppGlobal)
- modules(applicationModule)
- }
- }
-
- companion object {
- private lateinit var instance: AppGlobal
-
- val preferences: SharedPreferences by lazy {
- PreferenceManager.getDefaultSharedPreferences(instance)
- }
-
- var versionName = ""
- var versionCode = 0
- var screenWidth80 = 0
-
- val Instance: AppGlobal get() = instance
- val resources: Resources get() = Instance.resources
- val packageManager: PackageManager get() = Instance.packageManager
-
- var audioManager: AudioManager by Delegates.notNull()
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/common/Screens.kt b/app/src/main/kotlin/com/meloda/fast/common/Screens.kt
deleted file mode 100644
index b0719a89..00000000
--- a/app/src/main/kotlin/com/meloda/fast/common/Screens.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.meloda.fast.common
-
-import com.github.terrakok.cicerone.androidx.FragmentScreen
-import com.meloda.fast.api.model.VkGroup
-import com.meloda.fast.api.model.VkMessage
-import com.meloda.fast.api.model.VkUser
-import com.meloda.fast.api.model.domain.VkConversationDomain
-import com.meloda.fast.model.UpdateItem
-import com.meloda.fast.screens.chatinfo.ChatInfoFragment
-import com.meloda.fast.screens.conversations.ConversationsFragment
-import com.meloda.fast.screens.login.LoginFragment
-import com.meloda.fast.screens.main.MainFragment
-import com.meloda.fast.screens.messages.ForwardedMessagesFragment
-import com.meloda.fast.screens.messages.MessagesHistoryFragment
-import com.meloda.fast.screens.settings.SettingsFragment
-import com.meloda.fast.screens.updates.UpdatesFragment
-import com.meloda.fast.screens.userbanned.UserBannedFragment
-
-@Suppress("FunctionName")
-object Screens {
- fun Main() = FragmentScreen { MainFragment.newInstance() }
-
- fun Login() = FragmentScreen { LoginFragment.newInstance() }
-
- fun Conversations() = FragmentScreen { ConversationsFragment() }
-
- fun MessagesHistory(
- conversation: VkConversationDomain,
- user: VkUser?,
- group: VkGroup?
- ) = FragmentScreen { MessagesHistoryFragment.newInstance(conversation, user, group) }
-
- fun ForwardedMessages(
- conversation: VkConversationDomain,
- messages: List,
- profiles: HashMap = hashMapOf(),
- groups: HashMap = hashMapOf()
- ) = FragmentScreen {
- ForwardedMessagesFragment.newInstance(
- conversation, messages, profiles, groups
- )
- }
-
- fun ChatInfo(
- conversation: VkConversationDomain,
- user: VkUser?,
- group: VkGroup?
- ) = FragmentScreen { ChatInfoFragment.newInstance(conversation, user, group) }
-
- fun Updates(updateItem: UpdateItem? = null) =
- FragmentScreen { UpdatesFragment.newInstance(updateItem) }
-
- fun Settings() = FragmentScreen { SettingsFragment.newInstance() }
-
- fun UserBanned(
- memberName: String,
- message: String,
- restoreUrl: String,
- accessToken: String
- ) = FragmentScreen {
- UserBannedFragment.newInstance(
- memberName, message, restoreUrl, accessToken
- )
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt b/app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt
deleted file mode 100644
index 5ea0d868..00000000
--- a/app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.meloda.fast.common
-
-import com.meloda.fast.BuildConfig
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.ota.OtaGetLatestReleaseResponse
-import com.meloda.fast.data.ota.OtaApi
-import com.meloda.fast.model.UpdateActualUrl
-import com.meloda.fast.model.UpdateItem
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.launch
-import java.net.URLEncoder
-import kotlin.coroutines.CoroutineContext
-
-interface UpdateManager {
- val stateFlow: Flow
-
- fun checkUpdates(): Job
-}
-
-class UpdateManagerImpl(private val repo: OtaApi) : UpdateManager {
-
- private val coroutineContext: CoroutineContext
- get() = Dispatchers.IO
-
- private val coroutineScope = CoroutineScope(coroutineContext)
-
- private var otaBaseUrl: String? = null
-
- override val stateFlow = MutableStateFlow(UpdateManagerState.EMPTY)
-
- override fun checkUpdates() = coroutineScope.launch {
- val job: suspend () -> ApiAnswer = { repo.getActualUrl() }
-
- when (val jobResponse = job()) {
- is ApiAnswer.Success -> {
- val item = jobResponse.data
- otaBaseUrl = item.url
-
- getLatestRelease()
- }
- is ApiAnswer.Error -> {
- otaBaseUrl = null
- val throwable = jobResponse.error.throwable
-
- val newForm = stateFlow.value.copy(
- updateItem = null,
- throwable = throwable
- )
- stateFlow.emit(newForm)
- }
- }
- }
-
- private fun getLatestRelease() = coroutineScope.launch {
- val url = "$otaBaseUrl/releases-latest"
-
- val job: suspend () -> ApiAnswer> = {
- repo.getLatestRelease(url = url, secretCode = getOtaSecret())
- }
-
- when (val jobResponse = job()) {
- is ApiAnswer.Success -> {
- val response = jobResponse.data.response ?: return@launch
- val latestRelease = response.release
-
- val updateItem = if (latestRelease != null &&
- (AppGlobal.versionName
- .split("_")
- .getOrNull(1) != latestRelease.versionName ||
- AppGlobal.versionCode < latestRelease.versionCode)
- ) {
- latestRelease
- } else {
- null
- }
-
- val newForm = stateFlow.value.copy(
- updateItem = updateItem,
- throwable = null
- )
-
- stateFlow.emit(newForm)
- }
-
- is ApiAnswer.Error -> {
- val throwable = jobResponse.error.throwable
-
- val newForm = stateFlow.value.copy(
- updateItem = null,
- throwable = throwable
- )
- stateFlow.emit(newForm)
- }
- }
- }
-
- private fun getOtaSecret(): String {
- return URLEncoder.encode(BuildConfig.otaSecretCode, "utf-8")
- }
-}
-
-data class UpdateManagerState(
- val updateItem: UpdateItem?,
- val throwable: Throwable?,
-) {
- companion object {
- val EMPTY = UpdateManagerState(
- updateItem = null, throwable = null
- )
- }
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/common/di/ApplicationModule.kt b/app/src/main/kotlin/com/meloda/fast/common/di/ApplicationModule.kt
deleted file mode 100644
index 58784bac..00000000
--- a/app/src/main/kotlin/com/meloda/fast/common/di/ApplicationModule.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.meloda.fast.common.di
-
-import com.meloda.fast.di.apiModule
-import com.meloda.fast.di.dataModule
-import com.meloda.fast.di.databaseModule
-import com.meloda.fast.di.navigationModule
-import com.meloda.fast.di.networkModule
-import com.meloda.fast.di.otaModule
-import com.meloda.fast.screens.captcha.di.captchaModule
-import com.meloda.fast.screens.chatinfo.di.chatInfoModule
-import com.meloda.fast.screens.conversations.di.conversationsModule
-import com.meloda.fast.screens.login.di.loginModule
-import com.meloda.fast.screens.main.di.mainModule
-import com.meloda.fast.screens.messages.di.messagesHistoryModule
-import com.meloda.fast.screens.photos.di.photoViewModule
-import com.meloda.fast.screens.settings.di.settingsModule
-import com.meloda.fast.screens.twofa.di.twoFaModule
-import com.meloda.fast.screens.updates.di.updatesModule
-import org.koin.dsl.module
-
-val applicationModule = module {
- includes(
- navigationModule,
- databaseModule,
- dataModule,
- otaModule,
- networkModule,
- apiModule,
- loginModule,
- twoFaModule,
- captchaModule,
- mainModule,
- conversationsModule,
- chatInfoModule,
- settingsModule,
- updatesModule,
- messagesHistoryModule,
- photoViewModule,
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/compose/Dialogs.kt b/app/src/main/kotlin/com/meloda/fast/compose/Dialogs.kt
deleted file mode 100644
index 4f03a816..00000000
--- a/app/src/main/kotlin/com/meloda/fast/compose/Dialogs.kt
+++ /dev/null
@@ -1,163 +0,0 @@
-package com.meloda.fast.compose
-
-import androidx.compose.animation.*
-import androidx.compose.animation.core.tween
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import com.meloda.fast.ext.getString
-import com.meloda.fast.model.base.UiText
-import com.meloda.fast.ui.AppTheme
-
-@Composable
-fun MaterialDialog(
- onDismissAction: (() -> Unit),
- title: UiText? = null,
- message: UiText? = null,
- positiveText: UiText? = null,
- positiveAction: (() -> Unit)? = null,
- negativeText: UiText? = null,
- negativeAction: (() -> Unit)? = null,
- neutralText: UiText? = null,
- neutralAction: (() -> Unit)? = null,
- content: (@Composable () -> Unit)? = null
-) {
- var isVisible by remember {
- mutableStateOf(true)
- }
- val onDismissRequest = {
- onDismissAction.invoke()
- isVisible = false
- }
-
- AppTheme {
- // TODO: 08.04.2023, Danil Nikolaev: implement animation
- AlertAnimation(visible = isVisible) {
- Dialog(onDismissRequest = onDismissRequest) {
- val scrollState = rememberScrollState()
- val canScrollBackward by remember { derivedStateOf { scrollState.value > 0 } }
- val canScrollForward by remember { derivedStateOf { scrollState.value < scrollState.maxValue } }
-
- Surface(
- modifier = Modifier.fillMaxWidth(),
- color = AlertDialogDefaults.containerColor,
- shape = AlertDialogDefaults.shape,
- tonalElevation = AlertDialogDefaults.TonalElevation
- ) {
- Column(
- modifier = Modifier.padding(
- start = 20.dp,
- top = 20.dp,
- end = 20.dp,
- bottom = 10.dp
- )
- ) {
- Row {
- title?.getString()?.let { title ->
- Spacer(modifier = Modifier.width(4.dp))
- Text(
- text = title,
- style = MaterialTheme.typography.headlineSmall
- )
- }
- }
-
- if (canScrollBackward) {
- Divider(modifier = Modifier.fillMaxWidth())
- }
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f, fill = false)
- .verticalScroll(scrollState)
- ) {
- Spacer(modifier = Modifier.height(8.dp))
- Row {
- message?.getString()?.let { message ->
- Spacer(modifier = Modifier.width(4.dp))
- Text(
- text = message,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant
- )
- }
- }
-
- Spacer(modifier = Modifier.height(8.dp))
-
- content?.let { content ->
- Spacer(modifier = Modifier.height(4.dp))
- content.invoke()
- Spacer(modifier = Modifier.height(10.dp))
- }
- }
-
- if (canScrollForward) {
- Divider(modifier = Modifier.fillMaxWidth())
- }
-
- Row {
- neutralText?.getString()?.let { text ->
- TextButton(
- onClick = {
- onDismissRequest.invoke()
- neutralAction?.invoke()
- }
- ) {
- Text(text = text)
- }
- }
-
- Spacer(modifier = Modifier.weight(1f))
-
- negativeText?.getString()?.let { text ->
- TextButton(
- onClick = {
- onDismissRequest.invoke()
- negativeAction?.invoke()
- }
- ) {
- Text(text = text)
- }
- }
-
- Spacer(modifier = Modifier.width(2.dp))
-
- positiveText?.getString()?.let { text ->
- TextButton(
- onClick = {
- onDismissRequest.invoke()
- positiveAction?.invoke()
- }
- ) {
- Text(text = text)
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-@OptIn(ExperimentalAnimationApi::class)
-@Composable
-fun AlertAnimation(
- visible: Boolean,
- content: @Composable AnimatedVisibilityScope.() -> Unit
-) {
- AnimatedVisibility(
- visible = visible,
- enter = fadeIn(animationSpec = tween(400)) +
- scaleIn(animationSpec = tween(400)),
- exit = fadeOut(animationSpec = tween(150)),
- content = content
- )
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/data/account/AccountApi.kt b/app/src/main/kotlin/com/meloda/fast/data/account/AccountApi.kt
deleted file mode 100644
index bbd182e2..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/account/AccountApi.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.data.account
-
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.account.AccountUrls
-import retrofit2.http.GET
-import retrofit2.http.POST
-import retrofit2.http.QueryMap
-
-interface AccountApi {
-
- @GET(AccountUrls.SetOnline)
- suspend fun setOnline(@QueryMap params: Map): ApiAnswer>
-
- @POST(AccountUrls.SetOffline)
- suspend fun setOffline(@QueryMap params: Map): ApiAnswer>
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/account/AccountsDao.kt b/app/src/main/kotlin/com/meloda/fast/data/account/AccountsDao.kt
deleted file mode 100644
index bbd3f79f..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/account/AccountsDao.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.data.account
-
-import androidx.room.Dao
-import androidx.room.Insert
-import androidx.room.OnConflictStrategy
-import androidx.room.Query
-import com.meloda.fast.model.AppAccount
-
-@Dao
-interface AccountsDao {
-
- @Query("SELECT * FROM accounts")
- suspend fun getAll(): List
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- suspend fun insert(values: List)
-
- @Query("DELETE FROM accounts WHERE userId = :userId")
- suspend fun deleteById(userId: Int)
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/data/account/AccountsRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/account/AccountsRepository.kt
deleted file mode 100644
index cf427fab..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/account/AccountsRepository.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.meloda.fast.data.account
-
-import com.meloda.fast.api.network.account.AccountSetOfflineRequest
-import com.meloda.fast.api.network.account.AccountSetOnlineRequest
-
-class AccountsRepository(
- private val accountApi: AccountApi,
- private val accountsDao: AccountsDao
-) {
-
- suspend fun setOnline(params: AccountSetOnlineRequest) = accountApi.setOnline(params.map)
-
- suspend fun setOffline(params: AccountSetOfflineRequest) = accountApi.setOffline(params.map)
-
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosApi.kt b/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosApi.kt
deleted file mode 100644
index 1a3a33d3..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosApi.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.meloda.fast.data.audios
-
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.model.base.attachments.BaseVkAudio
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.audio.AudiosGetUploadServerResponse
-import com.meloda.fast.api.network.audio.AudiosUploadResponse
-import com.meloda.fast.api.network.audio.AudiosUrls
-import okhttp3.MultipartBody
-import retrofit2.http.*
-
-interface AudiosApi {
-
- @POST(AudiosUrls.GetUploadServer)
- suspend fun getUploadServer(): ApiAnswer>
-
- @Multipart
- @POST
- suspend fun upload(
- @Url url: String,
- @Part file: MultipartBody.Part
- ): ApiAnswer
-
- @FormUrlEncoded
- @POST(AudiosUrls.Save)
- suspend fun save(@FieldMap map: Map): ApiAnswer>
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosRepository.kt
deleted file mode 100644
index 5a6a145e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/audios/AudiosRepository.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.meloda.fast.data.audios
-
-import okhttp3.MultipartBody
-
-class AudiosRepository(
- private val audiosApi: AudiosApi
-) {
-
- suspend fun getUploadServer() = audiosApi.getUploadServer()
-
- suspend fun upload(url: String, file: MultipartBody.Part) = audiosApi.upload(url, file)
-
- suspend fun save(server: Int, audio: String, hash: String) = audiosApi.save(
- mapOf(
- "server" to server.toString(),
- "audio" to audio,
- "hash" to hash
- )
- )
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/auth/AuthApi.kt b/app/src/main/kotlin/com/meloda/fast/data/auth/AuthApi.kt
deleted file mode 100644
index feb0bf98..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/auth/AuthApi.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.meloda.fast.data.auth
-
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.auth.AuthDirectResponse
-import com.meloda.fast.api.network.auth.AuthUrls
-import com.meloda.fast.api.network.auth.SendSmsResponse
-import retrofit2.http.GET
-import retrofit2.http.Query
-import retrofit2.http.QueryMap
-
-interface AuthApi {
-
- @GET(AuthUrls.DirectAuth)
- suspend fun auth(@QueryMap param: Map): ApiAnswer
-
- @GET(AuthUrls.SendSms)
- suspend fun sendSms(@Query("sid") validationSid: String): ApiAnswer
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/auth/AuthRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/auth/AuthRepository.kt
deleted file mode 100644
index 9042d9e5..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/auth/AuthRepository.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.meloda.fast.data.auth
-
-import com.meloda.fast.api.network.auth.AuthDirectRequest
-
-class AuthRepository(
- private val authApi: AuthApi
-) {
-
- suspend fun auth(params: AuthDirectRequest) = authApi.auth(params.map)
-
- suspend fun sendSms(validationSid: String) = authApi.sendSms(validationSid)
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsApi.kt b/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsApi.kt
deleted file mode 100644
index 02b939e9..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsApi.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.meloda.fast.data.conversations
-
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.conversations.ConversationsGetResponse
-import com.meloda.fast.api.network.conversations.ConversationsUrls
-import retrofit2.http.FieldMap
-import retrofit2.http.FormUrlEncoded
-import retrofit2.http.POST
-
-interface ConversationsApi {
-
- @FormUrlEncoded
- @POST(ConversationsUrls.Get)
- suspend fun get(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(ConversationsUrls.Delete)
- suspend fun delete(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(ConversationsUrls.Pin)
- suspend fun pin(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(ConversationsUrls.Unpin)
- suspend fun unpin(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(ConversationsUrls.ReorderPinned)
- suspend fun reorderPinned(@FieldMap params: Map): ApiAnswer>
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsDao.kt b/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsDao.kt
deleted file mode 100644
index 0804a222..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsDao.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.meloda.fast.data.conversations
-
-import androidx.room.Dao
-import androidx.room.Insert
-import androidx.room.OnConflictStrategy
-import androidx.room.Query
-import com.meloda.fast.api.model.domain.VkConversationDomain
-
-@Dao
-interface ConversationsDao {
-
- @Query("SELECT * FROM conversations")
- suspend fun getAll(): List
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- suspend fun insert(values: List)
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsRepository.kt
deleted file mode 100644
index 9fd2049e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/conversations/ConversationsRepository.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.meloda.fast.data.conversations
-
-import com.meloda.fast.api.model.domain.VkConversationDomain
-import com.meloda.fast.api.network.conversations.ConversationsDeleteRequest
-import com.meloda.fast.api.network.conversations.ConversationsGetRequest
-import com.meloda.fast.api.network.conversations.ConversationsPinRequest
-import com.meloda.fast.api.network.conversations.ConversationsUnpinRequest
-
-class ConversationsRepository(
- private val conversationsApi: ConversationsApi,
- private val conversationsDao: ConversationsDao
-) {
-
- suspend fun get(params: ConversationsGetRequest) = conversationsApi.get(params.map)
-
- suspend fun delete(params: ConversationsDeleteRequest) = conversationsApi.delete(params.map)
-
- suspend fun pin(params: ConversationsPinRequest) = conversationsApi.pin(params.map)
-
- suspend fun unpin(params: ConversationsUnpinRequest) = conversationsApi.unpin(params.map)
-
- suspend fun store(conversations: List) = conversationsDao.insert(conversations)
-
-}
diff --git a/app/src/main/kotlin/com/meloda/fast/data/files/FilesApi.kt b/app/src/main/kotlin/com/meloda/fast/data/files/FilesApi.kt
deleted file mode 100644
index 73cdad7e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/files/FilesApi.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.meloda.fast.data.files
-
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.files.FilesGetMessagesUploadServerResponse
-import com.meloda.fast.api.network.files.FilesSaveFileResponse
-import com.meloda.fast.api.network.files.FilesUploadFileResponse
-import com.meloda.fast.api.network.files.FilesUrls
-import okhttp3.MultipartBody
-import retrofit2.http.*
-
-interface FilesApi {
-
- @FormUrlEncoded
- @POST(FilesUrls.GetMessagesUploadServer)
- suspend fun getUploadServer(
- @FieldMap map: Map
- ): ApiAnswer>
-
- @Multipart
- @POST
- suspend fun upload(
- @Url url: String,
- @Part file: MultipartBody.Part
- ): ApiAnswer
-
- @FormUrlEncoded
- @POST(FilesUrls.Save)
- suspend fun save(
- @FieldMap map: Map
- ): ApiAnswer>
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/files/FilesRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/files/FilesRepository.kt
deleted file mode 100644
index ec894800..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/files/FilesRepository.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.meloda.fast.data.files
-
-import com.google.gson.annotations.SerializedName
-import okhttp3.MultipartBody
-
-class FilesRepository(
- private val filesApi: FilesApi
-) {
-
- enum class FileType(val value: String) {
- @SerializedName("doc")
- File("doc"),
-
- @SerializedName("audio_message")
- VoiceMessage("audio_message")
- }
-
- suspend fun getMessagesUploadServer(peerId: Int, type: FileType) =
- filesApi.getUploadServer(
- mapOf(
- "peer_id" to peerId.toString(),
- "type" to type.value
- )
- )
-
- suspend fun uploadFile(url: String, file: MultipartBody.Part) = filesApi.upload(url, file)
-
- suspend fun saveMessageFile(file: String) = filesApi.save(mapOf("file" to file))
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsDao.kt b/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsDao.kt
deleted file mode 100644
index 87d7ae6e..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsDao.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.meloda.fast.data.groups
-
-import androidx.room.Dao
-import androidx.room.Insert
-import androidx.room.OnConflictStrategy
-import androidx.room.Query
-import com.meloda.fast.api.model.VkGroup
-
-@Dao
-interface GroupsDao {
-
- @Query("SELECT * FROM groups")
- suspend fun getAll(): List
-
- @Query("SELECT * FROM groups WHERE id = :id")
- suspend fun getById(id: Int): VkGroup?
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- suspend fun insert(values: List)
-
- suspend fun insert(values: Array) = insert(values.toList())
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsRepository.kt b/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsRepository.kt
deleted file mode 100644
index 50ba5c0d..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/groups/GroupsRepository.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.meloda.fast.data.groups
-
-class GroupsRepository(
- private val groupsDao: GroupsDao
-) {
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/longpoll/LongPollApi.kt b/app/src/main/kotlin/com/meloda/fast/data/longpoll/LongPollApi.kt
deleted file mode 100644
index a2ab6688..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/longpoll/LongPollApi.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.meloda.fast.data.longpoll
-
-import com.google.gson.JsonObject
-import com.meloda.fast.api.network.ApiAnswer
-import retrofit2.http.GET
-import retrofit2.http.QueryMap
-import retrofit2.http.Url
-
-interface LongPollApi {
-
- @GET
- suspend fun getResponse(
- @Url serverUrl: String,
- @QueryMap params: Map
- ): ApiAnswer
-
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/meloda/fast/data/messages/MessagesApi.kt b/app/src/main/kotlin/com/meloda/fast/data/messages/MessagesApi.kt
deleted file mode 100644
index c12d1fd0..00000000
--- a/app/src/main/kotlin/com/meloda/fast/data/messages/MessagesApi.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.meloda.fast.data.messages
-
-import com.meloda.fast.api.base.ApiResponse
-import com.meloda.fast.api.model.base.BaseVkChat
-import com.meloda.fast.api.model.base.BaseVkLongPoll
-import com.meloda.fast.api.model.base.BaseVkMessage
-import com.meloda.fast.api.network.ApiAnswer
-import com.meloda.fast.api.network.messages.MessagesGetByIdResponse
-import com.meloda.fast.api.network.messages.MessagesGetConversationMembersResponse
-import com.meloda.fast.api.network.messages.MessagesGetHistoryResponse
-import com.meloda.fast.api.network.messages.MessagesUrls
-import retrofit2.http.FieldMap
-import retrofit2.http.FormUrlEncoded
-import retrofit2.http.POST
-
-interface MessagesApi {
-
- @FormUrlEncoded
- @POST(MessagesUrls.GetHistory)
- suspend fun getHistory(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.Send)
- suspend fun send(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.MarkAsImportant)
- suspend fun markAsImportant(@FieldMap params: Map): ApiAnswer>>
-
- @FormUrlEncoded
- @POST(MessagesUrls.GetLongPollServer)
- suspend fun getLongPollServer(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.Pin)
- suspend fun pin(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.Unpin)
- suspend fun unpin(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.Delete)
- suspend fun delete(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.Edit)
- suspend fun edit(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.GetById)
- suspend fun getById(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.MarkAsRead)
- suspend fun markAsRead(@FieldMap params: Map): ApiAnswer>
-
- @FormUrlEncoded
- @POST(MessagesUrls.GetChat)
- suspend fun getChat(@FieldMap params: Map