diff --git a/.gitignore b/.gitignore index aa724b77..de251274 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ .externalNativeBuild .cxx local.properties +.idea \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 565a2383..00000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Fast \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 3c7772a0..00000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c..00000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 61a9130c..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/meloda.xml b/.idea/dictionaries/meloda.xml deleted file mode 100644 index b44a8b27..00000000 --- a/.idea/dictionaries/meloda.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - podcast - - - \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml deleted file mode 100644 index cd711a0e..00000000 --- a/.idea/discord.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 7863a219..00000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index a5f05cd8..00000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index d5d35ec4..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddf..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 80cbf240..00000000 --- a/app/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -plugins { - id 'com.android.application' - id 'kotlin-android' - id 'kotlin-kapt' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - applicationId "com.meloda.fast" - minSdkVersion 23 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - coreLibraryDesugaringEnabled true - - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() - } -} - -dependencies { - implementation project(":mvp") - implementation project(":vksdk") - implementation project(":arrayutils") - implementation project(":netservices") - implementation project(":concurrent") - implementation project(":extensions") - - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - - implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" - - implementation 'androidx.core:core-ktx:1.5.0-beta02' - - implementation 'androidx.appcompat:appcompat:1.3.0-beta01' - implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01' - implementation 'androidx.recyclerview:recyclerview:1.2.0-beta02' - implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.fragment:fragment-ktx:1.3.0' - implementation 'com.google.android.material:material:1.3.0' - - implementation 'androidx.room:room-runtime:2.3.0-beta02' - kapt 'androidx.room:room-compiler:2.3.0-beta02' - - implementation 'com.facebook.fresco:fresco:2.3.0' - - implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - - implementation 'com.squareup.picasso:picasso:2.71828' - - implementation 'com.github.rahatarmanahmed:circularprogressview:2.5.0' - - implementation 'com.google.code.gson:gson:2.8.6' - - implementation 'org.jsoup:jsoup:1.13.1' - - implementation 'ch.acra:acra:4.11.1' - - def appCenterSdkVersion = '4.1.0' - implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}" - implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}" - implementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}" -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 00000000..39bb676c --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,113 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-kapt") + id("androidx.navigation.safeargs.kotlin") + id("dagger.hilt.android.plugin") + id("kotlin-parcelize") +} + +android { + compileSdk = 31 + buildToolsVersion = "31.0.0" + + defaultConfig { + applicationId = "com.meloda.fast" + minSdk = 23 + targetSdk = 31 + versionCode = 1 + versionName = "1.0" + + javaCompileOptions { + annotationProcessorOptions { + arguments += mapOf("room.schemaLocation" to "$projectDir/schemas") + } + } + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + isCoreLibraryDesugaringEnabled = true + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + tasks.withType { + kotlinOptions { + jvmTarget = "1.8" + } + } + + buildFeatures { + dataBinding = true + viewBinding = true + } + +} + +kapt { + correctErrorTypes = true + + //use this shit if you don't want to have hilt errors + javacOptions { + option("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true") + } +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30") + + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") + + implementation("androidx.appcompat:appcompat:1.4.0-alpha03") + implementation("com.google.android.material:material:1.4.0") + implementation("androidx.core:core-ktx:1.7.0-alpha01") + implementation("androidx.preference:preference-ktx:1.1.1") + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01") + implementation("androidx.recyclerview:recyclerview:1.2.1") + implementation("androidx.cardview:cardview:1.0.0") + implementation("androidx.fragment:fragment-ktx:1.3.6") + + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1") + + implementation("androidx.room:room-runtime:2.3.0") + kapt("androidx.room:room-compiler:2.3.0") + + implementation("androidx.navigation:navigation-fragment-ktx:2.3.5") + implementation("androidx.navigation:navigation-ui-ktx:2.3.5") + + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1") + implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1") + implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1") + implementation("androidx.lifecycle:lifecycle-common-java8:2.3.1") + + implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.2") + implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2") + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") + + implementation("com.google.dagger:hilt-android:2.38.1") + kapt("com.google.dagger:hilt-android-compiler:2.38.1") + implementation("androidx.hilt:hilt-navigation-fragment:1.0.0") + + implementation("com.github.yogacp:android-viewbinding:1.0.2") + + implementation("io.coil-kt:coil:1.3.2") + + implementation("com.google.code.gson:gson:2.8.8") + implementation("org.jsoup:jsoup:1.14.2") + implementation("ch.acra:acra:4.11.1") + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..ff59496d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# proguardFiles setting in build.gradle.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/app/release/app-release.apk b/app/release/app-release.apk deleted file mode 100644 index 6034a02d..00000000 Binary files a/app/release/app-release.apk and /dev/null differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json deleted file mode 100644 index d7d1b834..00000000 --- a/app/release/output-metadata.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 2, - "artifactType": { - "type": "APK", - "kind": "Directory" - }, - "applicationId": "com.meloda.fast", - "variantName": "processReleaseResources", - "elements": [ - { - "type": "SINGLE", - "filters": [], - "versionCode": 1, - "versionName": "1.0", - "outputFile": "app-release.apk" - } - ] -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a5250609..e157a4f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,19 +1,24 @@ + + @@ -26,23 +31,10 @@ android:name=".service.LongPollService" android:enabled="true" /> - - - - - - - - - - + android:enabled="true" + android:exported="false"> diff --git a/app/src/main/java/com/meloda/fast/UserConfig.kt b/app/src/main/java/com/meloda/fast/UserConfig.kt deleted file mode 100644 index 80aa623b..00000000 --- a/app/src/main/java/com/meloda/fast/UserConfig.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.meloda.fast - -import android.text.TextUtils -import com.meloda.fast.common.AppGlobal - -object UserConfig { - - private const val TOKEN = "token" - private const val USER_ID = "user_id" - - const val API_ID = "6964679" - - var token = "" - var userId = 0 - - fun save() { - AppGlobal.preferences.edit() - .putString(TOKEN, token) - .putInt(USER_ID, userId) - .apply() - } - - fun restore() { - token = AppGlobal.preferences.getString(TOKEN, "") ?: "" - userId = AppGlobal.preferences.getInt(USER_ID, -1) - } - - fun clear() { - token = "" - userId = -1 - - AppGlobal.preferences.edit() - .remove(TOKEN) - .remove(USER_ID) - .apply() - } - - fun isLoggedIn(): Boolean { - return userId > 0 && !TextUtils.isEmpty(token) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/DropUserDataActivity.kt b/app/src/main/java/com/meloda/fast/activity/DropUserDataActivity.kt deleted file mode 100644 index d398bf0f..00000000 --- a/app/src/main/java/com/meloda/fast/activity/DropUserDataActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.meloda.fast.activity - -import android.content.Intent -import android.os.Bundle -import com.meloda.fast.UserConfig -import com.meloda.fast.base.BaseActivity - -class DropUserDataActivity : BaseActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - UserConfig.clear() - -// TaskManager.execute { AppGlobal.database.clearAllTables() } - - startActivity(Intent(this, MainActivity::class.java)) - finishAffinity() - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/LoginActivityDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/LoginActivityDeprecated.kt deleted file mode 100644 index 9fda684b..00000000 --- a/app/src/main/java/com/meloda/fast/activity/LoginActivityDeprecated.kt +++ /dev/null @@ -1,134 +0,0 @@ -package com.meloda.fast.activity - -import android.annotation.SuppressLint -import android.content.Intent -import android.graphics.Bitmap -import android.os.Bundle -import android.util.Log -import android.view.MenuItem -import android.webkit.* -import android.widget.ProgressBar -import androidx.core.view.isVisible -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.extensions.ContextExtensions.color -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.base.BaseActivity -import com.meloda.fast.widget.Toolbar -import com.meloda.vksdk.VKAuth - -class LoginActivityDeprecated : BaseActivity() { - - private lateinit var toolbar: Toolbar - private lateinit var progressBar: ProgressBar - private lateinit var webView: WebView - private lateinit var refreshLayout: SwipeRefreshLayout - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_login) - - initViews() - - prepareToolbar() - prepareRefreshLayout() - - prepareSettings() - - val url = VKAuth.getUrl(UserConfig.API_ID, VKAuth.settings) - - webView.loadUrl(url) - } - - private fun initViews() { - toolbar = findViewById(R.id.toolbar) - progressBar = findViewById(R.id.progressBar) - webView = findViewById(R.id.webView) - refreshLayout = findViewById(R.id.refreshLayout) - } - - @SuppressLint("SetJavaScriptEnabled") - private fun prepareSettings() { - webView.settings.javaScriptEnabled = true - webView.clearCache(true) - webView.webViewClient = VKWebClient() - - val cookieManager = CookieManager.getInstance() - cookieManager.setAcceptCookie(true) - } - - private fun prepareToolbar() { - setSupportActionBar(toolbar) - - toolbar.navigationIcon = drawable(R.drawable.ic_close).tint(color(R.color.accent)) - toolbar.setNavigationClickListener { onBackPressed() } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) onBackPressed() - return super.onOptionsItemSelected(item) - } - - private fun prepareRefreshLayout() { - refreshLayout.apply { - setColorSchemeColors(color(R.color.accent)) - setOnRefreshListener { - webView.reload() - isRefreshing = false - } - } - } - - private inner class VKWebClient : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - view.loadUrl(url) - return true - } - - override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - - progressBar.isVisible = true - view.isVisible = false - - parseUrl(url) - } - - override fun onPageFinished(view: WebView, url: String) { - super.onPageFinished(view, url) - - progressBar.isVisible = false - view.isVisible = true - } - - override fun onReceivedError( - view: WebView, - request: WebResourceRequest?, - error: WebResourceError? - ) { - super.onReceivedError(view, request, error) - Log.e("VKM WebView", error.toString()) - } - } - - private fun parseUrl(url: String) { - try { - if (url.startsWith(VKAuth.redirectUrl) && !url.contains("error=")) { - val auth = VKAuth.parseRedirectUrl(url) - val token = auth[0] - val id = auth[1].toInt() - - UserConfig.token = token - UserConfig.userId = id - UserConfig.save() - - finishAffinity() - startActivity(Intent(this, MainActivity::class.java)) - } - } catch (e: Exception) { - e.printStackTrace() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/MainActivity.kt b/app/src/main/java/com/meloda/fast/activity/MainActivity.kt deleted file mode 100644 index 4392b0f4..00000000 --- a/app/src/main/java/com/meloda/fast/activity/MainActivity.kt +++ /dev/null @@ -1,254 +0,0 @@ -package com.meloda.fast.activity - -import android.content.Intent -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import androidx.core.view.GravityCompat -import androidx.core.view.isVisible -import androidx.drawerlayout.widget.DrawerLayout -import com.google.android.material.bottomnavigation.BottomNavigationView -import com.google.android.material.navigation.NavigationView -import com.meloda.extensions.ContextExtensions.color -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.base.BaseActivity -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.common.FragmentSwitcher -import com.meloda.fast.common.TimeManager -import com.meloda.fast.dialog.AccountDialog -import com.meloda.fast.fragment.* -import com.meloda.fast.service.LongPollService -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.util.ViewUtils -import com.meloda.fast.widget.Toolbar -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.model.VKUser -import java.util.* - - -class MainActivity : BaseActivity(), - NavigationView.OnNavigationItemSelectedListener, - BottomNavigationView.OnNavigationItemSelectedListener { - - private lateinit var fragmentConversationsDeprecated: FragmentConversationsDeprecated - private lateinit var fragmentFriendsDeprecated: FragmentFriendsDeprecated - private lateinit var settingsFragment: SettingsFragment - - private var selectedId = 0 - - private lateinit var drawerLayout: DrawerLayout - lateinit var bottomBar: BottomNavigationView - private lateinit var navigationView: NavigationView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - initViews() - -// checkLogin() - - if (UserConfig.isLoggedIn()) { - VKApi.init(Locale.getDefault().language, UserConfig.token, AppGlobal.handler) - - supportFragmentManager.beginTransaction() - .replace(R.id.fragmentContainer, ChatsFragment()) - .commit() - } else { - bottomBar.isVisible = false - - supportFragmentManager.beginTransaction() - .replace(R.id.fragmentContainer, LoginFragment()) - .commit() - } - - -// TimeManager.init(this) - -// prepareFragments() - -// prepareNavigationView() -// prepareBottomBar() -// checkLogin() - } - - private fun initViews() { - drawerLayout = findViewById(R.id.drawerLayout) - bottomBar = findViewById(R.id.bottomBar) - navigationView = findViewById(R.id.navigationView) - } - - override fun onDestroy() { - TimeManager.destroy() - super.onDestroy() - } - - private fun prepareFragments() { - fragmentConversationsDeprecated = FragmentConversationsDeprecated() - fragmentFriendsDeprecated = FragmentFriendsDeprecated(UserConfig.userId) - settingsFragment = SettingsFragment() - - val containerId = R.id.fragmentContainer - - FragmentSwitcher.addFragments( - supportFragmentManager, - containerId, - listOf(fragmentConversationsDeprecated) - ) - } - - fun initToolbar(toolbar: Toolbar) { - toolbar.navigationIcon = - drawable(R.drawable.ic_search).tint(color(R.color.text_secondary_60_alpha)) - - toolbar.setTitleMode(Toolbar.TitleMode.HINT) - toolbar.setTitle(R.string.action_search) - toolbar.setAvatarClickListener { openAccountDialog() } - } - - private fun openAccountDialog() { - AccountDialog().show(supportFragmentManager, AccountDialog.TAG) - } - - private fun prepareNavigationView() { - navigationView.layoutParams?.width = AppGlobal.screenWidth - AppGlobal.screenWidth / 6 - - navigationView.setNavigationItemSelectedListener(this) - - drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) - } - - private fun prepareBottomBar() { -// val menu = bottomBar.menu -// -// val navigationFriends = menu.add(R.string.navigation_friends) -// navigationFriends.icon = drawable(R.drawable.ic_people_outline) -// -// val navigationConversations = menu.add(R.string.navigation_conversations) -// navigationConversations.icon = drawable(R.drawable.ic_message_outline) -// -// val navigationImportant = menu.add(R.string.navigation_important) -// navigationImportant.icon = drawable(R.drawable.ic_star_border) - - bottomBar.setOnNavigationItemSelectedListener(this) - } - - private fun createMenuItem(menu: Menu, tag: String): MenuItem { - return when (tag) { - "friends" -> - menu.add("Friends").apply { icon = drawable(R.drawable.ic_people_outline) } - "conversations" -> - menu.add("Conversations").apply { icon = drawable(R.drawable.ic_message_outline) } - "important" -> - menu.add("Important").apply { icon = drawable(R.drawable.ic_star_border) } - - else -> menu.add("") - } - } - - private fun checkLogin() { - if (UserConfig.isLoggedIn()) { - startLongPoll() - loadProfileInfo() - } else { - - } - } - - private fun openMainScreen() { - selectedId = R.id.navigationConversations - bottomBar.selectedItemId = selectedId - openConversationsScreen() - } - - private fun startLongPoll() { - startService(Intent(this, LongPollService::class.java)) - } - - private fun openConversationsScreen() { - FragmentSwitcher.showFragment( - supportFragmentManager, - fragmentConversationsDeprecated.javaClass.simpleName, - true - ) - } - - private fun openFriendsScreen() { - FragmentSwitcher.showFragment( - supportFragmentManager, - fragmentFriendsDeprecated.javaClass.simpleName, - true - ) - } - - private fun openSettingsScreen() { - startActivity(Intent(this, SettingsActivityDeprecated::class.java)) - } - - private fun loadProfileInfo() { - if (AndroidUtils.hasConnection()) { -// TaskManager.loadUser( -// VKApiKeys.UPDATE_USER, UserConfig.userId, -// object : OnResponseListener { -// override fun onResponse(response: VKUser) { -// prepareNavigationHeader(response) -// openMainScreen() -// } -// -// override fun onError(t: Throwable) { -// openMainScreen() -// } -// }) - } - } - - private fun prepareNavigationHeader(user: VKUser) { - ViewUtils.prepareNavigationHeader(navigationView.getHeaderView(0), user) - } - - override fun onNavigationItemSelected(item: MenuItem): Boolean { - switchFragment(item.itemId) - return true - } - - private fun switchFragment(itemId: Int) { - var valid = true - - when (itemId) { - R.id.navigationConversations -> { - openConversationsScreen() - } - R.id.navigationFriends -> { - openFriendsScreen() - } - R.id.navigationSettings -> { - openSettingsScreen() - } - else -> { - valid = false - } - } - - if (!valid) return - - if (selectedId != itemId) { - selectedId = itemId - navigationView.setCheckedItem(selectedId) - } - - if (drawerLayout.isDrawerOpen(GravityCompat.START)) { - drawerLayout.closeDrawer(GravityCompat.START) - } - } - - override fun onBackPressed() { - if (drawerLayout.isDrawerOpen(navigationView)) { - drawerLayout.closeDrawer(navigationView) - } else { - super.onBackPressed() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/MessagesActivityDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/MessagesActivityDeprecated.kt deleted file mode 100644 index 8da1b952..00000000 --- a/app/src/main/java/com/meloda/fast/activity/MessagesActivityDeprecated.kt +++ /dev/null @@ -1,402 +0,0 @@ -package com.meloda.fast.activity - -import android.content.res.ColorStateList -import android.graphics.Color -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import android.widget.* -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.widget.Toolbar -import androidx.core.view.isVisible -import androidx.core.widget.addTextChangedListener -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.amulyakhare.textdrawable.TextDrawable -import com.meloda.extensions.ContextExtensions.color -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.fast.R -import com.meloda.fast.activity.ui.presenter.MessagesPresenterDeprecated -import com.meloda.fast.activity.ui.view.MessagesViewDeprecated -import com.meloda.fast.base.BaseActivity -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.dialog.ProfileDialog -import com.meloda.fast.fragment.SettingsFragment -import com.meloda.fast.util.KeyboardUtils -import com.meloda.fast.util.TextUtils -import com.meloda.fast.util.ViewUtils -import com.meloda.fast.widget.CircleImageView -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKGroup -import com.meloda.vksdk.model.VKModel -import com.meloda.vksdk.model.VKUser - - -class MessagesActivityDeprecated : BaseActivity(), MessagesViewDeprecated { - - companion object { - const val TAG = "MessagesActivity" - - const val MESSAGES_COUNT = 30 - - const val TAG_EXTRA_TITLE = "title" - const val TAG_EXTRA_AVATAR = "avatar" - const val TAG_EXTRA_ID = "id" - const val TAG_EXTRA_USER = "user" - const val TAG_EXTRA_GROUP = "group" - } - - private var isEdit = false - - private var fabState = FabState.VOICE - - private enum class FabState { - VOICE, SEND, EDIT, DELETE, BLOCKED - } - - private var title = "" - private var avatar = "" - - private var lastMessageText = "" - private var attachments = arrayListOf() - - private var peerId = 0 - - private var dialogUser: VKUser? = null - private var dialogGroup: VKGroup? = null - - private lateinit var presenterDeprecated: MessagesPresenterDeprecated - - lateinit var recyclerView: RecyclerView - private lateinit var refreshLayout: SwipeRefreshLayout - private lateinit var toolbar: Toolbar - private lateinit var chatAvatar: CircleImageView - private lateinit var chatTitle: TextView - private lateinit var chatInfo: TextView - private lateinit var chatPanel: LinearLayout - private lateinit var chatMessage: EditText - private lateinit var chatSend: ImageButton - private lateinit var progressBar: ProgressBar - - private lateinit var noItemsView: LinearLayout - private lateinit var noInternetView: LinearLayout - private lateinit var errorView: LinearLayout - - override fun onDestroy() { - super.onDestroy() - presenterDeprecated.destroy() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_messages) - initViews() - - initExtraData() - - prepareToolbar() - prepareRefreshLayout() - prepareRecyclerView() - prepareEditText() - - presenterDeprecated = MessagesPresenterDeprecated(this) - presenterDeprecated.setup(peerId, recyclerView) - } - - private fun initViews() { - toolbar = findViewById(R.id.toolbar) - recyclerView = findViewById(R.id.recyclerView) - refreshLayout = findViewById(R.id.refreshLayout) - chatAvatar = findViewById(R.id.chatAvatar) - chatTitle = findViewById(R.id.chatTitle) - chatInfo = findViewById(R.id.chatInfo) - chatPanel = findViewById(R.id.chatPanel) - chatMessage = findViewById(R.id.chatMessage) - chatSend = findViewById(R.id.chatSend) - progressBar = findViewById(R.id.progressBar) - - noItemsView = findViewById(R.id.noItemsView) - noInternetView = findViewById(R.id.noInternetView) - errorView = findViewById(R.id.errorView) - } - - private fun initExtraData() { - peerId = intent.getIntExtra(TAG_EXTRA_ID, -1) - title = intent.getStringExtra(TAG_EXTRA_TITLE) ?: "" - avatar = intent.getStringExtra(TAG_EXTRA_AVATAR) ?: "" - - dialogUser = intent.getSerializableExtra(TAG_EXTRA_USER) as VKUser? - dialogGroup = intent.getSerializableExtra(TAG_EXTRA_GROUP) as VKGroup? - } - - private fun prepareToolbar() { - setSupportActionBar(toolbar) - - val placeholder = TextDrawable - .builder() - .buildRound(TextUtils.getFirstLetterFromString(title), color(R.color.accent)) - - chatAvatar.setImageDrawable(placeholder) - -// chatAvatar.loadImage(avatar, placeholder) - - toolbar.setOnClickListener { presenterDeprecated.openProfile() } - - chatAvatar.setOnClickListener { presenterDeprecated.openProfile() } - - chatTitle.text = title - - supportActionBar?.setDisplayShowTitleEnabled(false) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - - toolbar.navigationIcon.tint(color(R.color.accent)) - } - - private fun prepareRefreshLayout() { - refreshLayout.isEnabled = false - } - - private fun prepareRecyclerView() { - recyclerView.layoutManager = - LinearLayoutManager(this, RecyclerView.VERTICAL, false).also { - it.stackFromEnd = true - } - - recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - - if (dy < 0 && AppGlobal.inputMethodManager.isAcceptingText && AppGlobal.preferences.getBoolean( - SettingsFragment.KEY_HIDE_KEYBOARD_ON_SCROLL_UP, - true - ) - ) { - KeyboardUtils.hideKeyboardFrom(chatMessage) - } - } - }) - } - - private fun prepareEditText() { - chatMessage.addTextChangedListener { - fabState = if (it.toString().trim().isEmpty()) { - if (isEdit) { - FabState.DELETE - } else { - FabState.VOICE - } - } else { - if (isEdit) { - FabState.EDIT - } else { - FabState.SEND - } - } - - refreshFabStyle() - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.activity_messages, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> onBackPressed() - - R.id.messagesRefresh -> { - presenterDeprecated.updateData() - } - } - - return super.onOptionsItemSelected(item) - } - - private fun refreshFabStyle() { - chatSend.isClickable = true - - when (fabState) { - FabState.VOICE -> { - chatSend.apply { - setImageResource(R.drawable.ic_mic) - - setOnClickListener { - showVoiceRecordingTip() - } - setOnLongClickListener { - true - } - } - } - FabState.SEND -> { - chatSend.apply { - setImageResource(R.drawable.ic_send) - - setOnClickListener { - presenterDeprecated.sendMessage(chatMessage.text.toString(), attachments) - } - - setOnLongClickListener { - presenterDeprecated.sendMessage(chatMessage.text.toString(), attachments, false) - true - } - } - } - FabState.EDIT -> { - chatSend.apply { - setImageResource(R.drawable.ic_done) - - setOnClickListener { - //editMessage() - } - - setOnLongClickListener { - performClick() - true - } - } - - } - FabState.DELETE -> { - chatSend.apply { - setImageResource(R.drawable.ic_trash_outline) - - chatSend.setOnClickListener { - //deleteMessage - } - - chatSend.setOnLongClickListener { - performClick() - true - } - } - } - FabState.BLOCKED -> { - chatSend.apply { - isClickable = false - setImageResource(R.drawable.ic_lock) - } - } - } - } - - override fun showChatPanel() { - chatPanel.isVisible = true - } - - override fun hideChatPanel() { - chatPanel.isVisible = false - } - - override fun setWritingAllowed(allowed: Boolean) { - if (allowed) { - fabState = FabState.VOICE - - chatSend.imageTintList = ColorStateList.valueOf(color(R.color.accent)) - - chatMessage.isEnabled = true - - chatPanel.setBackgroundResource(R.drawable.chat_panel_background) - } else { - fabState = FabState.BLOCKED - - chatSend.imageTintList = ColorStateList.valueOf(Color.WHITE) - - chatMessage.isEnabled = false - chatMessage.setHintTextColor(Color.WHITE) - chatMessage.setHint(R.string.no_access) - - chatPanel.setBackgroundResource(R.drawable.chat_panel_background_blocked) - } - } - - override fun setChatInfo(info: String) { - chatInfo.text = info - chatInfo.isVisible = info.isNotEmpty() - } - - override fun openProfile(conversation: VKConversation) { - conversation.let { - val profileDialog = ProfileDialog(it, title) - profileDialog.show(supportFragmentManager, ProfileDialog.TAG) - } - } - - override fun showErrorLoadConversationAlert() { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.error_occurred) - builder.setMessage(R.string.error_loading_message) - builder.setPositiveButton(R.string.retry) { _, _ -> - presenterDeprecated.loadConversation(peerId) - } - builder.setNegativeButton(R.string.no) { _, _ -> onBackPressed() } - builder.setCancelable(false) - builder.show() - } - - override fun showVoiceRecordingTip() { - Toast.makeText(this, R.string.voice_record_tip, Toast.LENGTH_LONG).show() - } - - override fun setMessageText(text: String) { - chatMessage.setText(text) - } - - override fun showErrorSnackbar(t: Throwable) { - ViewUtils.showErrorSnackbar(getRootView(), t) - } - - override fun prepareNoItemsView() { - } - - override fun showNoItemsView() { - noItemsView.isVisible = true - } - - override fun hideNoItemsView() { - noItemsView.isVisible = false - } - - override fun prepareNoInternetView() { - } - - override fun showNoInternetView() { - noInternetView.isVisible = true - } - - override fun hideNoInternetView() { - noInternetView.isVisible = false - } - - override fun prepareErrorView() { - } - - override fun showErrorView() { - errorView.isVisible = true - } - - override fun hideErrorView() { - errorView.isVisible = false - } - - override fun showProgressBar() { - progressBar.isVisible = true - } - - override fun hideProgressBar() { - progressBar.isVisible = false - } - - override fun showRefreshLayout() { - refreshLayout.isRefreshing = true - } - - override fun hideRefreshLayout() { - refreshLayout.isRefreshing = false - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/SettingsActivityDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/SettingsActivityDeprecated.kt deleted file mode 100644 index 2c964d52..00000000 --- a/app/src/main/java/com/meloda/fast/activity/SettingsActivityDeprecated.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.meloda.fast.activity - -import android.os.Bundle -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.fast.R -import com.meloda.fast.base.BaseActivity -import com.meloda.fast.common.FragmentSwitcher -import com.meloda.fast.fragment.SettingsFragment -import com.meloda.fast.util.ColorUtils -import com.meloda.fast.widget.Toolbar - -class SettingsActivityDeprecated : BaseActivity() { - - private lateinit var toolbar: Toolbar - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_settings) - initViews() - - setSupportActionBar(toolbar) - - toolbar.setNavigationClickListener { onBackPressed() } - - toolbar.navigationIcon = drawable(R.drawable.ic_arrow_back) - toolbar.tintNavigationIcon(ColorUtils.getColorAccent(this)) - - supportFragmentManager.beginTransaction() - .replace(R.id.fragmentContainer, SettingsFragment()) - .commit() - } - - private fun initViews() { - toolbar = findViewById(R.id.toolbar) - } - - override fun onBackPressed() { - val currentFragment = FragmentSwitcher.getCurrentFragment(supportFragmentManager) ?: return - - if (currentFragment.javaClass == SettingsFragment::class.java && (currentFragment as SettingsFragment).onBackPressed()) { - super.onBackPressed() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/UpdateActivityDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/UpdateActivityDeprecated.kt deleted file mode 100644 index eab5b026..00000000 --- a/app/src/main/java/com/meloda/fast/activity/UpdateActivityDeprecated.kt +++ /dev/null @@ -1,350 +0,0 @@ -package com.meloda.fast.activity - -import android.app.Activity -import android.app.DownloadManager -import android.content.Intent -import android.content.IntentFilter -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.os.Environment -import android.util.Log -import android.view.View -import android.widget.LinearLayout -import android.widget.TextView -import androidx.appcompat.app.AlertDialog -import androidx.core.content.FileProvider -import androidx.core.text.HtmlCompat -import androidx.core.view.isVisible -import com.github.rahatarmanahmed.cpv.CircularProgressView -import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton -import com.meloda.concurrent.TaskManager -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.FloatExtensions.int -import com.meloda.fast.BuildConfig -import com.meloda.fast.R -import com.meloda.fast.base.BaseActivity -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.common.UpdateManager -import com.meloda.fast.model.NewUpdateInfo -import com.meloda.fast.receiver.DownloadUpdateReceiver -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.util.TimeUtils -import com.meloda.vksdk.OnResponseListener -import java.io.File -import java.text.SimpleDateFormat -import java.util.* - - -class UpdateActivityDeprecated : BaseActivity() { - - companion object { - private const val FILE_BASE_PATH = "file://" - private const val MIME_TYPE = "application/vnd.android.package-archive" - private const val PROVIDER_PATH = ".provider" - } - - private var isChecking = false - private var isNewUpdate = false - private var isDownloading = false - - private var downloadId = 0L - - private var lastCheckTime = 0L - - private var newUpdate = NewUpdateInfo() - - private lateinit var updateCheckUpdates: ExtendedFloatingActionButton - private lateinit var updateState: TextView - private lateinit var updateVersion: TextView - private lateinit var updateInfo: TextView - private lateinit var updateInfoLayout: LinearLayout - private lateinit var updateProgress: LinearLayout - private lateinit var updateProgressBar: CircularProgressView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_update) - initViews() - - updateProgressBar.maxProgress = 100F - - lastCheckTime = AppGlobal.preferences.getLong("updateCheckTime", 0) - - refreshState() - - checkUpdates() - - updateCheckUpdates.setOnClickListener { - lastCheckTime = System.currentTimeMillis() - AppGlobal.preferences.edit().putLong("updateCheckTime", lastCheckTime).apply() - - checkUpdates() - } - } - - private fun initViews() { - updateCheckUpdates = findViewById(R.id.updateCheckUpdates) - updateInfo = findViewById(R.id.updateInfo) - updateVersion = findViewById(R.id.updateVersion) - updateState = findViewById(R.id.updateState) - updateInfoLayout = findViewById(R.id.updateInfoLayout) - updateProgress = findViewById(R.id.updateProgress) - updateProgressBar = updateProgress.getChildAt(0) as CircularProgressView - } - - private fun installUpdate(context: Activity, file: File) { - val install = Intent(Intent.ACTION_VIEW) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) - install.data = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID + PROVIDER_PATH, - file - ) - } else { - install.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - install.setDataAndType(Uri.fromFile(file), MIME_TYPE) - } - - context.startActivity(install) -// context.finishAffinity() - } - - private fun downloadUpdate() { - checkIsInstallingAllowed() - - val timer = Timer() - - updateCheckUpdates.shrink() - updateCheckUpdates.isClickable = false - - isDownloading = true - refreshState() - - TaskManager.execute { - val apkName = newUpdate.version - - val destination = - getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/$apkName.apk" - - val uri = Uri.parse("$FILE_BASE_PATH$destination") - - val file = File(destination) - if (file.exists()) file.delete() - - val request = DownloadManager.Request(Uri.parse(newUpdate.downloadLink)) - - request.setTitle("${getString(R.string.app_name)} ${apkName}.apk") - request.setMimeType(MIME_TYPE) - request.setDestinationUri(uri) - request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE) - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) - - val receiver = DownloadUpdateReceiver() - receiver.listener = object : OnResponseListener { - override fun onResponse(response: Any?) { - timer.cancel() - - installUpdate(this@UpdateActivityDeprecated, file) - - unregisterReceiver(receiver) - - runOnUiThread { - updateProgressBar.isIndeterminate = true - - updateCheckUpdates.extend() - updateCheckUpdates.isClickable = true - - isDownloading = false - refreshState() - } - } - - override fun onError(t: Throwable) { - } - } - - registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) - - downloadId = AppGlobal.downloadManager.enqueue(request) - - timer.schedule(object : TimerTask() { - override fun run() { - val query = DownloadManager.Query() - query.setFilterById(downloadId) - - val cursor = AppGlobal.downloadManager.query(query) - if (cursor.moveToFirst()) { - val sizeIndex = - cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES) - val downloadedIndex = - cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR) - val size = cursor.getInt(sizeIndex) - val downloaded = cursor.getInt(downloadedIndex) - - val progress = if (size != -1) (downloaded * 100.0F / size) else 0.0F - - Log.d("Downloading update", "progress $progress%") - - if (progress.int() > 0) { - runOnUiThread { - if (updateProgressBar.isIndeterminate) { - updateProgressBar.isIndeterminate = false - updateProgressBar.stopAnimation() - } - - updateProgressBar.progress = progress - } - } - } - } - - }, 0, 1000) - } - } - - private fun checkUpdates() { - if (isChecking) return - - isChecking = true - refreshState() - - UpdateManager.checkUpdates(object : UpdateManager.OnUpdateListener { - override fun onNewUpdate(updateInfo: NewUpdateInfo) { - isChecking = false - isNewUpdate = true - - this@UpdateActivityDeprecated.newUpdate = updateInfo - - refreshState() - } - - override fun onNoUpdates() { - isNewUpdate = false - isChecking = false - - this@UpdateActivityDeprecated.newUpdate = NewUpdateInfo() - - refreshState() - } - }) - } - - private fun checkIsInstallingAllowed() { - if (!AndroidUtils.isCanInstallUnknownApps(this)) { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.warning) - builder.setMessage(R.string.update_unknown_sources_disabled_message) - builder.setPositiveButton(R.string.yes) { _, _ -> - AndroidUtils.openInstallUnknownAppsScreen(this) - } - builder.setNegativeButton(R.string.no, null) - builder.show() - } - } - - private fun refreshState() { - when { - isChecking -> { - updateState.text = getString(R.string.update_state_checking) - - setAlpha(updateInfoLayout, true) - setAlpha(updateProgress, false) - setAlpha(updateCheckUpdates, true) - } - isDownloading -> { - updateState.text = getString(R.string.update_state_downloading) - - setAlpha(updateInfoLayout, true) - setAlpha(updateProgress, false) - setAlpha(updateCheckUpdates, true) - } - else -> { - if (isNewUpdate) { - updateCheckUpdates.text = getString(R.string.update_download) - updateCheckUpdates.icon = drawable(R.drawable.ic_file_download) - } else { - updateCheckUpdates.text = getString(R.string.update_check_updates) - updateCheckUpdates.icon = drawable(R.drawable.ic_refresh) - } - - updateCheckUpdates.setOnClickListener { - if (isNewUpdate) { - downloadUpdate() - } else { - checkUpdates() - } - } - - updateState.text = - getString(if (isNewUpdate) R.string.update_state_update_available else R.string.update_state_no_updates) - - updateVersion.text = - if (isNewUpdate) - getString( - R.string.update_new_version, - newUpdate.version, - newUpdate.code - ) - else getString( - R.string.update_current_version, - AppGlobal.versionName, - AppGlobal.versionCode - ) - - updateInfo.text = - when { - isNewUpdate -> if (newUpdate.changelog.isEmpty()) "" else getString( - R.string.update_changelog, - HtmlCompat.fromHtml( - newUpdate.changelog, - HtmlCompat.FROM_HTML_MODE_LEGACY - ) - ) - lastCheckTime.toString().isEmpty() || lastCheckTime == 0L -> "" - else -> getString(R.string.update_last_check_time, getCheckTime()) - } - - setAlpha(updateInfoLayout, false) - setAlpha(updateProgress, true) - setAlpha(updateCheckUpdates, false) - } - } - } - - private fun getCheckTime(): String { - val time = lastCheckTime - - val lastTime = TimeUtils.removeTime(Date(time)) - val currentTime = TimeUtils.removeTime(Date(System.currentTimeMillis())) - - val format = if (currentTime > lastTime) { - "dd.MM.yyyy HH:mm" - } else { - "HH:mm" - } - - return SimpleDateFormat(format, Locale.getDefault()).format(time) - } - - private fun setAlpha(view: View, toZero: Boolean) { - if (toZero) { - view.animate() - .alpha(0F) - .setDuration(250) - .withEndAction { view.isVisible = false } - .start() - } else { - view.animate() - .alpha(1F) - .setDuration(250) - .withStartAction { view.isVisible = true } - .start() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/ui/presenter/MessagesPresenterDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/ui/presenter/MessagesPresenterDeprecated.kt deleted file mode 100644 index 1315330e..00000000 --- a/app/src/main/java/com/meloda/fast/activity/ui/presenter/MessagesPresenterDeprecated.kt +++ /dev/null @@ -1,254 +0,0 @@ -package com.meloda.fast.activity.ui.presenter - -import androidx.recyclerview.widget.RecyclerView -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.activity.ui.repository.MessagesRepositoryDeprecated -import com.meloda.fast.activity.ui.view.MessagesViewDeprecated -import com.meloda.fast.adapter.MessagesAdapterDeprecated -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.listener.ItemLongClickListener -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpPresenter -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.model.VKModel -import kotlin.random.Random - -class MessagesPresenterDeprecated(viewState: MessagesViewDeprecated) : - MvpPresenter( - viewState, - MessagesRepositoryDeprecated::class.java.name - ), - ItemClickListener, - ItemLongClickListener, - TaskManager.OnEventListener { - - companion object { - const val DEFAULT_MESSAGES_COUNT = 30 - } - - private lateinit var adapter: MessagesAdapterDeprecated - private lateinit var conversation: VKConversation - - private var peerId: Int = -1 - - private var lastMessageText: String = "" - - private lateinit var recyclerView: RecyclerView - - override fun destroy() { - adapter.destroy() - } - - fun setup(peerId: Int, recyclerView: RecyclerView) { - this.peerId = peerId - this.recyclerView = recyclerView - this.context = recyclerView.context - - viewState.showProgressBar() - getCachedConversation(peerId) - } - - fun updateData() { - adapter.clear() - loadMessages(peerId) - } - - fun openProfile() { - viewState.openProfile(conversation) - } - - private fun createAdapter() { - adapter = MessagesAdapterDeprecated(context!!, arrayListOf(), conversation).also { - it.itemClickListener = this - it.itemLongClickListener = this - } - - recyclerView.adapter = adapter - } - - private fun getCachedConversation(peerId: Int) { - repository.getCachedConversation(peerId, object : MvpOnResponseListener { - override fun onResponse(response: VKConversation) { - conversation = response - - createAdapter() - refreshConversation(response) - - getCachedMessages(peerId, 0, DEFAULT_MESSAGES_COUNT, - object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - loadConversation(peerId) - loadMessages(peerId) - } - - override fun onError(t: Throwable) { - loadConversation(peerId) - loadMessages(peerId) - } - }) - } - - override fun onError(t: Throwable) { - loadConversation(peerId) - loadMessages(peerId) - } - }) - } - - fun loadConversation(peerId: Int) { - if (adapter.isNotEmpty()) { - viewState.hideProgressBar() - } - - repository.loadConversation(peerId, object : MvpOnResponseListener { - - override fun onResponse(response: VKConversation) { - conversation = response - - createAdapter() - refreshConversation(response) - } - - override fun onError(t: Throwable) { - viewState.hideProgressBar() - viewState.showErrorLoadConversationAlert() - } - - }) - } - - private fun refreshConversation(conversation: VKConversation) { - checkIsWritingAllowed(conversation) - - repository.getChatInfo( - conversation, - object : MvpOnResponseListener { - override fun onResponse(response: String) { - viewState.setChatInfo(response) - } - - override fun onError(t: Throwable) { - viewState.setChatInfo(AppGlobal.resources.getString(R.string.error_obtain_chat_info)) - } - }) - } - - private fun checkIsWritingAllowed(conversation: VKConversation) { - if (conversation.isGroupChannel) { - viewState.hideChatPanel() - return - } - - viewState.showChatPanel() - viewState.setWritingAllowed(conversation.isAllowed) - } - - private fun getCachedMessages( - peerId: Int, - offset: Int = 0, - count: Int = DEFAULT_MESSAGES_COUNT, - listener: MvpOnResponseListener? = null - ) { - repository.getCachedMessages(peerId, offset, count, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - viewState.hideProgressBar() - fillAdapter(response, offset) - - listener?.onResponse(null) - } - - override fun onError(t: Throwable) { - if (adapter.isEmpty()) { - viewState.showProgressBar() - } - - listener?.onError(t) - } - }) - } - - private fun loadMessages(peerId: Int, offset: Int = 0, count: Int = DEFAULT_MESSAGES_COUNT) { - repository.loadMessages(peerId, offset, count, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - fillAdapter(response, offset) - } - - override fun onError(t: Throwable) { - - } - - }) - } - - private fun fillAdapter( - messages: ArrayList, - offset: Int - ) { - if (adapter.isEmpty()) adapter.isNotCachedValues = true - if (offset == 0) { - adapter.updateValues(messages) - } else { - adapter.addAll(messages) - } - - adapter.notifyDataSetChanged() - - if (offset == 0) recyclerView.scrollToPosition(adapter.itemCount - 1) - } - - override fun onItemClick(position: Int) { - - } - - override fun onItemLongClick(position: Int) { - - } - - override fun onNewEvent(info: EventInfo<*>) { - - } - - fun sendMessage( - text: String = "", - attachments: ArrayList = arrayListOf(), - scrollToBottom: Boolean = true - ) { - lastMessageText = text - - val message = VKMessage().also { - it.date = (System.currentTimeMillis() / 1000).toInt() - it.text = text - it.isOut = true - it.peerId = peerId - it.fromId = UserConfig.userId - it.randomId = Random.nextInt() - } - - viewState.setMessageText("") - - adapter.addMessage(message, true, scrollToBottom) - - repository.sendMessage(peerId, text, message.randomId, object : MvpOnResponseListener { - override fun onResponse(response: Int) { - message.id = response - -// TaskManager.execute { MemoryCache.put(message) } -// TaskManager.loadMessage(VKApiKeys.UPDATE_MESSAGE, response) - } - - override fun onError(t: Throwable) { - viewState.showErrorSnackbar(t) - - viewState.setMessageText(lastMessageText) - } - }) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/ui/repository/MessagesRepositoryDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/ui/repository/MessagesRepositoryDeprecated.kt deleted file mode 100644 index 40fe8696..00000000 --- a/app/src/main/java/com/meloda/fast/activity/ui/repository/MessagesRepositoryDeprecated.kt +++ /dev/null @@ -1,170 +0,0 @@ -package com.meloda.fast.activity.ui.repository - -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.common.AppGlobal -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpRepository -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.VKConstants -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.util.VKUtil -import java.util.* - -class MessagesRepositoryDeprecated : MvpRepository() { - - fun loadMessages( - peerId: Int, - offset: Int, - count: Int, - listener: MvpOnResponseListener> - ) { - TaskManager.execute { - VKApi.messages() - .getHistory() - .peerId(peerId) - .reversed(false) - .extended(true) - .fields(VKConstants.USER_FIELDS + "," + VKConstants.GROUP_FIELDS) - .offset(offset) - .count(count) - .executeArray( - VKMessage::class.java, - object : OnResponseListener> { - override fun onResponse(response: ArrayList) { - TaskManager.execute { - cacheLoadedMessages(response) - -// MemoryCache.putUsers(VKMessage.profiles) -// MemoryCache.putGroups(VKMessage.groups) -// MemoryCache.putConversations(VKMessage.conversations) - - VKUtil.sortMessagesByDate(response, false) - - sendResponse(listener, response) - } - } - - override fun onError(t: Throwable) { - sendError(listener, t) - } - }) - } - } - - fun getCachedMessages( - peerId: Int, offset: Int, count: Int, - listener: MvpOnResponseListener> - ) { - TaskManager.execute { -// val messages = MemoryCache.getMessagesByPeerId(peerId).asArrayList() -// -// if (messages.isEmpty()) { -// sendError(listener, NullPointerException("Messages is empty")) -// return@execute -// } -// -// VKUtil.sortMessagesByDate(messages, false) -// -// val preparedMessages = ArrayUtils.cut(messages, offset, count) -// -// sendResponseArray(listener, preparedMessages) - } - } - - fun getCachedConversation(peerId: Int, listener: MvpOnResponseListener) { - TaskManager.execute { -// val conversation = MemoryCache.getConversationById(peerId) -// -// if (conversation == null) { -// sendError( -// listener, -// NullPointerException("Conversation is not cached at the moment") -// ) -// } else { -// sendResponse(listener, conversation) -// } - } - } - - fun loadConversation(peerId: Int, listener: MvpOnResponseListener) { -// TaskManager.loadConversation( -// VKApiKeys.UPDATE_CONVERSATION, -// peerId, -// object : OnResponseListener { -// override fun onResponse(response: VKConversation) { -// sendResponse(listener, response) -// } -// -// override fun onError(t: Throwable) { -// sendError(listener, t) -// } -// }) - } - - fun getChatInfo(conversation: VKConversation, listener: MvpOnResponseListener) { - when (conversation.type) { - VKConversation.Type.CHAT -> { - sendResponse( - listener, - AppGlobal.resources.getString( - if (conversation.isGroupChannel) - R.string.group_channel_members - else R.string.chat_members, - conversation.membersCount - ) - ) - } - VKConversation.Type.USER -> { -// val user = VKUtil.searchUser(conversation.conversationId, -// object : OnResponseListener { -// override fun onResponse(response: VKUser) { -// sendResponse(listener, VKUtil.getUserOnline(response)) -// } -// -// override fun onError(t: Throwable) { -// sendError(listener, t) -// } -// }) -// -// user?.let { -// sendResponse(listener, VKUtil.getUserOnline(it)) -// } - } - else -> { - sendResponse(listener, "") - } - } - } - - fun sendMessage( - peerId: Int, - message: String, - randomId: Int, - listener: MvpOnResponseListener - ) { - TaskManager.execute { - VKApi.messages() - .send() - .peerId(peerId) - .message(message) - .randomId(randomId) - .executeArray(Int::class.java, object : OnResponseListener> { - override fun onResponse(response: ArrayList) { - val messageId = response[0] - sendResponse(listener, messageId) - } - - override fun onError(t: Throwable) { - sendError(listener, t) - } - }) - } - } - - private fun cacheLoadedMessages(messages: ArrayList) { -// MemoryCache.putMessages(messages) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/activity/ui/view/MessagesViewDeprecated.kt b/app/src/main/java/com/meloda/fast/activity/ui/view/MessagesViewDeprecated.kt deleted file mode 100644 index ae5a28e8..00000000 --- a/app/src/main/java/com/meloda/fast/activity/ui/view/MessagesViewDeprecated.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.meloda.fast.activity.ui.view - -import com.meloda.mvp.MvpView -import com.meloda.vksdk.model.VKConversation - -interface MessagesViewDeprecated : MvpView { - - fun showChatPanel() - - fun hideChatPanel() - - fun setWritingAllowed(allowed: Boolean) - - fun setChatInfo(info: String) - - fun openProfile(conversation: VKConversation) - - fun showErrorLoadConversationAlert() - - fun showVoiceRecordingTip() - - fun setMessageText(text: String) - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/ChatsAdapter.kt b/app/src/main/java/com/meloda/fast/adapter/ChatsAdapter.kt deleted file mode 100644 index 4e16ebf3..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/ChatsAdapter.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.meloda.fast.adapter - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.VKLongPollParser -import com.meloda.fast.adapter.diffutil.ConversationsCallback -import com.meloda.fast.base.BaseAdapter -import com.meloda.fast.base.BaseHolder -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage - -class ChatsAdapter(context: Context, values: ArrayList) : - BaseAdapter( - context, values - ), - TaskManager.OnEventListener, - VKLongPollParser.OnMessagesListener { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(view(R.layout.item_conversation, parent)) - } - - override fun notifyChanges(oldList: List, newList: List) { - val callback = ConversationsCallback(oldList, newList) - val diff = DiffUtil.calculateDiff(callback) - - diff.dispatchUpdatesTo(this) - } - - override fun onNewEvent(info: EventInfo<*>) { - - } - - inner class ViewHolder(v: View) : BaseHolder(v) { - - override fun bind(position: Int, payloads: MutableList?) { - val conversation = getItem(position) - val lastMessage = conversation.lastMessage - - TaskManager.execute { - - } - } - - } - - override fun onNewMessage(message: VKMessage) { - TODO("Not yet implemented") - } - - override fun onEditMessage(message: VKMessage) { - TODO("Not yet implemented") - } - - override fun onRestoredMessage(message: VKMessage) { - TODO("Not yet implemented") - } - - override fun onDeleteMessage(peerId: Int, messageId: Int) { - TODO("Not yet implemented") - } - - override fun onReadMessage(peerId: Int, messageId: Int) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/ConversationsAdapterDeprecated.kt b/app/src/main/java/com/meloda/fast/adapter/ConversationsAdapterDeprecated.kt deleted file mode 100644 index ab7d6808..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/ConversationsAdapterDeprecated.kt +++ /dev/null @@ -1,696 +0,0 @@ -package com.meloda.fast.adapter - -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.net.Uri -import android.text.SpannableString -import android.text.TextUtils -import android.text.style.ForegroundColorSpan -import android.util.Log -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ImageView -import android.widget.TextView -import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.extensions.ContextExtensions.color -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated -import com.meloda.fast.base.BaseAdapter -import com.meloda.fast.base.BaseHolder -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.database.CacheStorage -import com.meloda.fast.util.VKUtils -import com.meloda.fast.widget.CircleImageView -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApiKeys -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKGroup -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.model.VKUser -import com.meloda.vksdk.util.VKUtil - - -@Suppress("UNCHECKED_CAST") -class ConversationsAdapterDeprecated( - val recyclerView: RecyclerView, - values: ArrayList -) : BaseAdapter( - recyclerView.context, - values -), TaskManager.OnEventListener { - - companion object { - private const val TAG = "ConversationsAdapter" - } - - var isLoading: Boolean = false - private var currentPosition: Int = -1 - - init { - TaskManager.addOnEventListener(this) - } - - override fun destroy() { - TaskManager.removeOnEventListener(this) - } - - override fun onNewEvent(info: EventInfo<*>) { - when (info.key) { - VKApiKeys.NEW_MESSAGE.name -> addMessage(info.data as VKMessage) - VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage) - VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage) - VKApiKeys.READ_MESSAGE.name -> readMessage( - (info.data as Array)[0], - (info.data as Array)[1] - ) - VKApiKeys.DELETE_MESSAGE.name -> deleteMessage( - (info.data as Array)[0], - (info.data as Array)[1] - ) - - VKApiKeys.UPDATE_CONVERSATION.name -> updateConversation(info.data as Int) - VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int) - VKApiKeys.UPDATE_USER.name -> updateUsers(info.data as ArrayList) - VKApiKeys.UPDATE_GROUP.name -> updateGroups(info.data as ArrayList) - - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConversationHolder { - return ConversationHolder(view(R.layout.item_conversation, parent)) - } - - override fun onBindViewHolder( - holder: ConversationHolder, - position: Int, - payloads: MutableList - ) { - currentPosition = position - initListeners(holder.itemView, position) - holder.bind(position, payloads) - } - - fun isLastItem() = currentPosition >= itemCount - 1 - - inner class ConversationHolder(v: View) : BaseHolder(v) { - - private var attachments: ImageView = v.findViewById(R.id.conversationTextAttachment) - private var text: TextView = v.findViewById(R.id.conversationText) - private var title: TextView = v.findViewById(R.id.conversationTitle) - private var avatar: ImageView = v.findViewById(R.id.conversationAvatar) - private var online: ImageView = v.findViewById(R.id.conversationUserOnline) - private var out: CircleImageView = v.findViewById(R.id.conversationOut) - private var counter: TextView = v.findViewById(R.id.conversationCounter) - private var date: TextView = v.findViewById(R.id.conversationDate) - private var type: ImageView = v.findViewById(R.id.conversationType) - private var userAvatar: ImageView = v.findViewById(R.id.conversationUserAvatar) - private var root: FrameLayout = v.findViewById(R.id.conversationRoot) - - private val colorHighlight = context.color(R.color.accent) - - override fun bind(position: Int) { - bind(position, mutableListOf()) - } - - override fun bind(position: Int, payloads: MutableList?) { - Log.d(TAG, "bind position: $position") - - val conversation = getItem(position) - val lastMessage = conversation.lastMessage - - TaskManager.execute { - val peerUser: VKUser? = - if (conversation.isUser()) CacheStorage.usersStorage.getUser(conversation.id) else null - - val peerGroup: VKGroup? = - if (conversation.isGroup()) CacheStorage.groupsStorage.getGroup(conversation.id) else null - - val fromUser: VKUser? = - if (lastMessage.isFromUser()) CacheStorage.usersStorage.getUser(lastMessage.fromId) else null - - val fromGroup: VKGroup? = - if (lastMessage.isFromGroup()) CacheStorage.groupsStorage.getGroup(lastMessage.fromId) else null - - conversation.peerUser = peerUser - conversation.peerGroup = peerGroup - - lastMessage.fromUser = fromUser - lastMessage.fromGroup = fromGroup - - post { - val dialogTitle = setTitle(conversation, peerUser, peerGroup) - - if (payloads != null && payloads.isNotEmpty()) { - for (payload in payloads) { - when (payload) { - ConversationsCallbackDeprecated.CONVERSATION -> { - setUserOnline(conversation, peerUser) - prepareUserAvatar( - conversation, - lastMessage, - fromUser, - fromGroup - ) - prepareAvatar(dialogTitle, conversation, peerUser, peerGroup) - setDialogType(conversation) - setIsRead(lastMessage, conversation) - setCounterBackground(conversation) - } - ConversationsCallbackDeprecated.MESSAGE -> { - prepareUserAvatar( - conversation, - lastMessage, - fromUser, - fromGroup - ) - prepareAttachments(lastMessage) - setIsRead(lastMessage, conversation) - setDate(lastMessage) - } - ConversationsCallbackDeprecated.GROUP -> { - prepareAvatar(dialogTitle, conversation, peerUser, peerGroup) - } - ConversationsCallbackDeprecated.USER -> { - setUserOnline(conversation, peerUser) - prepareAvatar(dialogTitle, conversation, peerUser, peerGroup) - } - ConversationsCallbackDeprecated.EDIT_MESSAGE -> { - prepareUserAvatar( - conversation, - lastMessage, - fromUser, - fromGroup - ) - prepareAttachments(lastMessage) - setIsRead(lastMessage, conversation) - setDate(lastMessage) - } - ConversationsCallbackDeprecated.DATE -> { - setDate(lastMessage) - } - ConversationsCallbackDeprecated.ONLINE -> { - setUserOnline(conversation, peerUser) - } - ConversationsCallbackDeprecated.ATTACHMENTS -> { - prepareAttachments(lastMessage) - } - ConversationsCallbackDeprecated.AVATAR -> { - prepareAvatar(dialogTitle, conversation, peerUser, peerGroup) - } - ConversationsCallbackDeprecated.USER_AVATAR -> { - prepareUserAvatar( - conversation, - lastMessage, - fromUser, - fromGroup - ) - } - ConversationsCallbackDeprecated.READ -> { - setIsRead(lastMessage, conversation) - } - ConversationsCallbackDeprecated.NOTIFICATIONS -> { - setCounterBackground(conversation) - } - } - } - - return@post - } - - setUserOnline(conversation, peerUser) - - prepareUserAvatar(conversation, lastMessage, fromUser, fromGroup) - - prepareAvatar(dialogTitle, conversation, peerUser, peerGroup) - - setDialogType(conversation) - - prepareAttachments(lastMessage) - - setIsRead(lastMessage, conversation) - - setDate(lastMessage) - - setCounterBackground(conversation) - - root.isVisible = true - } - } - } - - private fun setTitle( - conversation: VKConversation, - peerUser: VKUser?, - peerGroup: VKGroup? - ): String { - val dialogTitle = VKUtil.getTitle(conversation, peerUser, peerGroup) - title.text = dialogTitle - - return dialogTitle - } - - private fun setUserOnline(conversation: VKConversation, peerUser: VKUser?) { - val onlineIcon = VKUtils.getUserOnlineIcon(context, conversation, peerUser) - - online.setImageDrawable(onlineIcon) - online.isVisible = onlineIcon != null - } - - private fun prepareUserAvatar( - conversation: VKConversation, - lastMessage: VKMessage, - fromUser: VKUser?, - fromGroup: VKGroup? - ) { - if ((conversation.isChat() || lastMessage.isOut) && !conversation.isGroupChannel) { - userAvatar.isVisible = true - - val avatar = VKUtil.getUserAvatar(lastMessage, fromUser, fromGroup) - - if (avatar.isEmpty()) { - userAvatar.setImageDrawable(ColorDrawable(Color.TRANSPARENT)) - } else { - userAvatar.setImageURI(Uri.parse(avatar)) - } - } else { - userAvatar.isVisible = false - userAvatar.setImageDrawable(null) - } - } - - private fun prepareAvatar( - dialogTitle: String, - conversation: VKConversation, - peerUser: VKUser?, - peerGroup: VKGroup? - ) { - val dialogAvatarPlaceholder = VKUtils.getAvatarPlaceholder(context, dialogTitle) - - avatar.setImageDrawable(dialogAvatarPlaceholder) - - val avatarLink = VKUtil.getAvatar(conversation, peerUser, peerGroup) - - if (avatarLink.isNotEmpty()) { - avatar.setImageURI(Uri.parse(avatarLink)) - } - } - - private fun setDialogType(conversation: VKConversation) { -// val dDialogType = VKUtil.getDialogType(context, conversation) -// -// type.setImageDrawable(dDialogType) -// type.isVisible = dDialogType != null -// type.isVisible = false - } - - private fun prepareAttachments(lastMessage: VKMessage) { -// text.apply { -// compoundDrawablePadding = 0 -// setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null) -// } - attachments.isVisible = false - - if (lastMessage.action == null) { - when { - lastMessage.attachments.isNotEmpty() -> { - val attachmentString = - VKUtils.getAttachmentText(context, lastMessage.attachments) - - val attachmentText = - if (lastMessage.text.isEmpty()) attachmentString else lastMessage.text - - val startIndex = - if (lastMessage.text.isEmpty()) 0 else lastMessage.text.length - - val span = SpannableString(attachmentText).apply { - setSpan( - ForegroundColorSpan(colorHighlight), - startIndex, - attachmentText.length, - 0 - ) - } - - val attachmentDrawable = - VKUtils.getAttachmentDrawable(context, lastMessage.attachments) - text.text = span - - attachments.isVisible = true - attachments.setImageDrawable(attachmentDrawable) - -// text.apply { -// text = span -// setCompoundDrawablesRelativeWithIntrinsicBounds( -// attachmentDrawable, -// null, -// null, -// null -// ) -// compoundDrawablePadding = 8 -// } - } - lastMessage.fwdMessages.isNotEmpty() -> { - val fwdText = - VKUtils.getFwdText(context, lastMessage.getForwardedMessages()) - val span = SpannableString(fwdText).apply { - setSpan(ForegroundColorSpan(colorHighlight), 0, fwdText.length, 0) - } - - text.text = span - } - else -> { - text.text = if (text.maxLines == 1) lastMessage.text.replace( - "\n", - " " - ) else lastMessage.text - } - } - } else { - VKUtils.getActionText(context, lastMessage, - object : OnResponseListener { - override fun onResponse(response: String) { - val span = SpannableString(response).apply { - setSpan( - ForegroundColorSpan(colorHighlight), - 0, - response.length, - 0 - ) - } - - text.text = span - } - - override fun onError(t: Throwable) { - TODO("Not yet implemented") - } - - }) - - } - - if (lastMessage.attachments.isEmpty() && lastMessage.fwdMessages.isEmpty() && lastMessage.action == null && TextUtils.isEmpty( - lastMessage.text - ) - ) { - val unknown = "..." - val span = SpannableString(unknown).apply { - setSpan(ForegroundColorSpan(colorHighlight), 0, unknown.length, 0) - } - - text.text = span - } - } - - private fun setIsRead(lastMessage: VKMessage, conversation: VKConversation) { - val isRead = - ((lastMessage.isOut && conversation.outReadMessageId == conversation.lastMessageId || - !lastMessage.isOut && conversation.inReadMessageId == conversation.lastMessageId) && conversation.lastMessageId == lastMessage.id) && conversation.unreadCount == 0 - - if (isRead) { - counter.visibility = View.GONE - out.visibility = View.GONE - } else { - if (lastMessage.isOut) { - out.visibility = View.VISIBLE - counter.visibility = View.GONE - counter.text = "" - } else { - out.visibility = View.GONE - counter.visibility = View.VISIBLE - counter.text = conversation.unreadCount.toString() - } - } - } - - private fun setDate(lastMessage: VKMessage) { - val dateText = VKUtils.getTime(context, lastMessage) - date.text = dateText - } - - private fun setCounterBackground(conversation: VKConversation) { - counter.background.setTint(if (conversation.isNotificationsDisabled()) Color.GRAY else colorHighlight) - } - } - - @Deprecated("Message is bad") - private fun addMessage(message: VKMessage) { - val index = searchConversationIndex(message.peerId) - - val oldList = ArrayList(values) - - if (index >= 0) { - val currentConversation = this[index] - - val conversation = prepareConversation(currentConversation, message) - - removeAt(index) - add(0, conversation) - notifyChanges(oldList) - } else { -// TaskManager.loadConversation( -// VKApiKeys.UPDATE_CONVERSATION, -// message.peerId, -// null -// ) - - TaskManager.execute { -// val cachedConversation = MemoryCache.getConversationById(message.peerId) -// if (cachedConversation != null) { -// add(0, prepareConversation(cachedConversation, message)) -// post { notifyChanges(oldList) } -// return@execute -// } - - val tempConversations = VKConversation().apply { - id = message.peerId - - localId = - if (VKUtil.isChatId(id)) id - 2000000000 else id - type = - if (id < 0) VKConversation.Type.GROUP else if (id > 2000000000) VKConversation.Type.CHAT else VKConversation.Type.USER - - lastMessage = message - lastMessageId = message.id - } - - add(0, tempConversations) - - post { notifyChanges(oldList) } - } - } - - val firstVisiblePosition = - (recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - - if (firstVisiblePosition <= 1) recyclerView.scrollToPosition(0) - } - - private fun editMessage(message: VKMessage) { - val index = searchConversationIndex(message.peerId) - if (index == -1) return - - val conversation = getItem(index) - - if (conversation.lastMessageId != message.id) return - - conversation.lastMessage = message - - notifyItemChanged(index, ConversationsCallbackDeprecated.EDIT_MESSAGE) - } - - private fun readMessage(peerId: Int, messageId: Int) { - val index = searchConversationIndex(peerId) - if (index == -1) return - - val conversation = getItem(index) - val message = conversation.lastMessage - - if (message.isInbox()) { - conversation.inReadMessageId = messageId - } else { - conversation.outReadMessageId = messageId - } - - conversation.unreadCount = if (conversation.lastMessageId == messageId) { - 0 - } else { - conversation.lastMessageId - messageId - } - - notifyItemChanged(index, ConversationsCallbackDeprecated.READ) - } - - @Deprecated("Need to rewrite") - private fun deleteMessage(peerId: Int, messageId: Int) { - return - val index = searchConversationIndex(peerId) - if (index == -1) return - - val oldList = ArrayList(values) - - val oldDialog = values[index] - - val dialog = oldDialog.clone() - -// TaskManager.execute { -// val cachedMessages = MemoryCache.getMessagesByPeerId(dialog.conversationId) -// val messages = VKUtil.sortMessagesByDate(ArrayList(cachedMessages), true) -// -// if (messages.isEmpty()) { -// MemoryCache.deleteConversation(dialog.conversationId) -// -// AppGlobal.post { -// removeAt(index) -// notifyChanges(oldList) -// } -// } else { -// val lastMessage = messages[0] -// -// dialog.lastMessageId = lastMessage.messageId -// dialog.lastMessage = lastMessage -// -// set(index, dialog) -// -// VKUtil.sortConversationsByDate(values, true) -// -// AppGlobal.post { -// notifyChanges(oldList) -// } -// } -// } - } - - @Deprecated("Message is bad") - private fun restoreMessage(message: VKMessage) { - val index = searchConversationIndex(message.peerId) - if (index == -1) return - - val oldList = ArrayList().apply { addAll(values) } - val oldDialog = values[index] - - val dialog = oldDialog.clone() - -// TaskManager.execute { -// val messages = -// MemoryCache.getMessagesByPeerId(dialog.conversationId).apply { addMessage(message) } -// -// VKUtil.sortMessagesByDate(ArrayList(messages), true) -// -// val lastMessage = messages[0] -// -// dialog.lastMessageId = lastMessage.messageId -// dialog.lastMessage = lastMessage -// -// set(index, dialog) -// -// VKUtil.sortConversationsByDate(values, true) -// -// AppGlobal.handler.post { -// notifyChanges(oldList) -// -// fragmentConversations.presenter.checkListIsEmpty(values) -// } -// } - } - - private fun prepareConversation( - conversation: VKConversation, - newMessage: VKMessage - ): VKConversation { - conversation.lastMessage = newMessage - conversation.lastMessageId = newMessage.id - - if (newMessage.isOut) { - conversation.unreadCount = 0 - newMessage.isRead = false - } else { - conversation.unreadCount++ - } - - if (newMessage.peerId == newMessage.fromId && newMessage.fromId == UserConfig.userId) { //для лс - conversation.outReadMessageId = newMessage.id - } - - return conversation - } - - private fun searchConversationIndex(peerId: Int): Int { - for (i in values.indices) { - if (getItem(i).id == peerId) return i - } - return -1 - } - - private fun searchMessageIndex(messageId: Int): Int { - for (i in values.indices) { - if (getItem(i).lastMessageId == messageId) return i - } - return -1 - } - - private fun updateConversation(peerId: Int) { - val index = searchConversationIndex(peerId) - if (index == -1) return - -// TaskManager.execute { -// val conversation = MemoryCache.getConversationById(peerId) ?: return@execute -// -// set(index, conversation) -// -// AppGlobal.post { -// notifyItemChanged( -// index, -// ConversationsCallbackDeprecated.CONVERSATION -// ) -// } -// } - } - - private fun updateGroups(groupIds: ArrayList) { - for (groupId in groupIds) { - val index = searchConversationIndex(groupId) - if (index == -1) return - - notifyItemChanged(index) - } - } - - private fun updateUsers(userIds: ArrayList) { - for (userId in userIds) { - val index = searchConversationIndex(userId) - if (index == -1) return - - notifyItemChanged(index) - } - } - - private fun updateMessage(messageId: Int) { - val index = searchMessageIndex(messageId) - if (index == -1) return - - - TaskManager.execute { - val conversation = getItem(index).clone() - -// conversation.apply { -// lastMessageId = messageId -// lastMessage = MemoryCache.getMessageById(messageId) ?: return@execute -// } - - AppGlobal.handler.post { - notifyItemChanged( - index, - ConversationsCallbackDeprecated.MESSAGE - ) - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/MessagesAdapterDeprecated.kt b/app/src/main/java/com/meloda/fast/adapter/MessagesAdapterDeprecated.kt deleted file mode 100644 index f26d73cc..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/MessagesAdapterDeprecated.kt +++ /dev/null @@ -1,584 +0,0 @@ -package com.meloda.fast.adapter - -import android.content.Context -import android.util.Log -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.extensions.FloatExtensions.int -import com.meloda.fast.BuildConfig -import com.meloda.fast.R -import com.meloda.fast.activity.MessagesActivityDeprecated -import com.meloda.fast.base.BaseAdapter -import com.meloda.fast.base.BaseHolder -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.widget.BoundedLinearLayout -import com.meloda.fast.widget.CircleImageView -import com.meloda.vksdk.VKApiKeys -import com.meloda.vksdk.model.* -import com.meloda.vksdk.util.VKUtil -import java.text.SimpleDateFormat -import java.util.* -import kotlin.math.abs - - -@Suppress("UNCHECKED_CAST") -class MessagesAdapterDeprecated( - context: Context, - values: ArrayList, - var conversation: VKConversation - -) : BaseAdapter(context, values), - TaskManager.OnEventListener { - - companion object { - private const val TYPE_FOOTER = 10101 - - private const val TYPE_NORMAL_IN = 7910 - private const val TYPE_NORMAL_OUT = 7911 - - private const val TYPE_ATTACHMENT_IN = 7920 - private const val TYPE_ATTACHMENT_OUT = 7921 - - private const val TYPE_ACTION = 7930 - - private const val TYPE_NORMAL_CHANNEL = 7940 - - const val TAG = "MessagesAdapter" - } - - private var recyclerView = (context as MessagesActivityDeprecated).recyclerView - private var layoutManager = recyclerView.layoutManager as LinearLayoutManager - - var isNotCachedValues = false - - init { - TaskManager.addOnEventListener(this) - } - - override fun destroy() { - TaskManager.removeOnEventListener(this) - } - - override fun onNewEvent(info: EventInfo<*>) { - when (info.key) { - VKApiKeys.NEW_MESSAGE.name -> addMessage(info.data as VKMessage) - - VKApiKeys.READ_MESSAGE.name -> readMessage( - (info.data as Array)[0], - (info.data as Array)[1] - ) - - VKApiKeys.RESTORE_MESSAGE.name -> restoreMessage(info.data as VKMessage) - VKApiKeys.EDIT_MESSAGE.name -> editMessage(info.data as VKMessage) - VKApiKeys.DELETE_MESSAGE.name -> deleteMessage( - (info.data as Array)[0], - (info.data as Array)[1] - ) - - VKApiKeys.UPDATE_MESSAGE.name -> updateMessage(info.data as Int) - VKApiKeys.UPDATE_USER.name -> updateUser(info.data as ArrayList) - VKApiKeys.UPDATE_GROUP.name -> updateGroup(info.data as ArrayList) - - else -> return - } - } - - override fun getItemCount(): Int { - return values.size + 1 - } - - override fun getItemViewType(position: Int): Int { - if (position == values.size) return TYPE_FOOTER - - val message = getItem(position) - - return when { - message.action != null -> TYPE_ACTION - conversation.isGroupChannel -> TYPE_NORMAL_CHANNEL - message.isOut && message.attachments.isEmpty() && message.fwdMessages.isEmpty() -> TYPE_NORMAL_OUT - !message.isOut && message.attachments.isEmpty() && message.fwdMessages.isEmpty() -> TYPE_NORMAL_IN - message.isOut && (message.attachments.isNotEmpty() || message.fwdMessages.isNotEmpty()) -> TYPE_ATTACHMENT_OUT - !message.isOut && (message.attachments.isNotEmpty() || message.fwdMessages.isNotEmpty()) -> TYPE_ATTACHMENT_IN - else -> 0 - } - } - - override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int): Holder { - return when (type) { - TYPE_FOOTER -> FooterHolder(generateEmptyView()) - - TYPE_NORMAL_IN -> ItemNormalIn(view(R.layout.item_message_normal_in, viewGroup)) - TYPE_NORMAL_OUT -> ItemNormalOut(view(R.layout.item_message_normal_out, viewGroup)) - - TYPE_ATTACHMENT_IN -> ItemAttachmentIn( - view( - R.layout.item_message_attachment_in, - viewGroup - ) - ) - TYPE_ATTACHMENT_OUT -> ItemAttachmentOut( - view( - R.layout.item_message_attachment_out, - viewGroup - ) - ) - - TYPE_ACTION -> ItemAction(view(R.layout.item_message_action, viewGroup)) - - TYPE_NORMAL_CHANNEL -> ItemChannel(view(R.layout.item_message_channel, viewGroup)) - - else -> PlaceHolder(view(R.layout.item_message, viewGroup)) - } - } - - override fun onBindViewHolder(holder: Holder, position: Int) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "bind position: $position") - } - - if (holder is FooterHolder) return - - super.onBindViewHolder(holder, position) - if (!isNotCachedValues) return - - val message = this[position] - - if (message.isRead.not()) { -// TaskManager.readMessage( -// VKApiKeys.READ_MESSAGE, -// conversation.conversationId, -// message.messageId -// ) - } - } - - private fun generateEmptyView(): View { - return View(context).also { - it.isFocusable = false - it.isClickable = false - it.isEnabled = false - it.layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - if (conversation.isGroupChannel) 0 else AndroidUtils.px(74f).int() - ) - } - } - - inner class FooterHolder(v: View) : Holder(v) { - override fun bind(position: Int) {} - } - - inner class ItemChannel(v: View) : ItemNormalIn(v) { - private val title: TextView = v.findViewById(R.id.channelTitle) - - override fun bind(position: Int) { - val message = getItem(position) - - ViewController().prepareDate(message, date) - - val avatarString = conversation.photo100 - -// val placeHolder = VKUtil.getAvatarPlaceholder(context, conversation.title) - -// avatar.setImageDrawable(placeHolder) -// ImageUtils.loadImage(avatarString, avatar, placeHolder) - - title.text = conversation.title - - text.text = message.text - - root.visibility = View.VISIBLE - } - } - - inner class ItemAction(v: View) : Holder(v) { - private val text: TextView = v.findViewById(R.id.messageAction) - - override fun bind(position: Int) { - val message = getItem(position) - - TaskManager.execute { - val user = searchUser(message) - val group = searchGroup(message) - - val name = - (if (group == null && !VKUtil.isGroupId(message.fromId)) user?.firstName else group?.name) - ?: "null" - -// VKUtil.getActionText(context, message, object : OnResponseListener { -// -// override fun onResponse(response: String) { -// val actionText = "$name $response" -// -// val spannable = SpannableString(actionText) -// spannable.setSpan(StyleSpan(Typeface.BOLD), 0, name.length, 0) -// -// text.text = spannable -// } -// -// override fun onError(t: Throwable) { -// } -// }) - - post { text.isVisible = true } - } - } - } - - open inner class ItemNormalIn(v: View) : NormalViewHolder(v) { - - override fun bind(position: Int) { - val message = getItem(position) - - TaskManager.execute { - val user = searchUser(message) - val group = searchGroup(message) - - post { - ViewController().apply { - prepareText(message, bubble, text) - prepareDate(message, date) - prepareAvatar(message, avatar) - loadAvatarImage(message, user, group, avatar) - } - - root.isVisible = true - } - } - } - } - - inner class ItemAttachmentIn(v: View) : ItemNormalIn(v) { - val attachments: LinearLayout = v.findViewById(R.id.messageAttachments) - - override fun bind(position: Int) { - super.bind(position) - - val message = getItem(position) - - AttachmentInflater.showAttachments(message, this) - } - } - - open inner class ItemNormalOut(v: View) : NormalViewHolder(v) { - - override fun bind(position: Int) { - val message = getItem(position) - - TaskManager.execute { - val user = searchUser(message) - val group = searchGroup(message) - - post { - ViewController().apply { - prepareText(message, bubble, text) - prepareDate(message, date) - prepareAvatar(message, avatar) - loadAvatarImage(message, user, group, avatar) - } - - root.isVisible = true - } - } - } - } - - inner class ItemAttachmentOut(v: View) : ItemNormalOut(v) { - - val attachments: LinearLayout = v.findViewById(R.id.messageAttachments) - - override fun bind(position: Int) { - super.bind(position) - - val message = getItem(position) - - AttachmentInflater.showAttachments(message, this) - } - - } - - abstract inner class NormalViewHolder(v: View) : Holder(v) { - protected val date: TextView = v.findViewById(R.id.messageDate) - protected val text: TextView = v.findViewById(R.id.messageText) - protected val root: LinearLayout = v.findViewById(R.id.messageRoot) - protected val bubble: BoundedLinearLayout = v.findViewById(R.id.messageBubble) - protected val avatar: CircleImageView = v.findViewById(R.id.messageAvatar) - } - - object AttachmentInflater { - fun showAttachments(message: VKMessage, holder: NormalViewHolder) { - val attachments = - (if (holder is ItemAttachmentOut) holder.attachments else if (holder is ItemAttachmentIn) holder.attachments else null) - ?: return - - if (message.fwdMessages.isNotEmpty() || message.attachments.isNotEmpty()) { - attachments.visibility = View.VISIBLE - attachments.removeAllViews() - } else { - attachments.visibility = View.GONE - } - - if (message.attachments.isNotEmpty()) { - prepareAttachments(message, attachments) - } - - if (message.fwdMessages.isNotEmpty()) { - prepareForwardedMessages(message, attachments) - } - } - - private fun prepareAttachments(message: VKMessage, attachments: LinearLayout) { - for (attachment in message.attachments) { - when (attachment) { - is VKPhoto -> photo(message, attachments) - is VKVideo -> video(message, attachments) - is VKLink -> link(message, attachments) - is VKAudio -> audio(message, attachments) - is VKDocument -> doc(message, attachments) - } - } - } - - private fun prepareForwardedMessages(message: VKMessage, attachments: LinearLayout) { - - } - - fun link(message: VKMessage, attachments: LinearLayout) { - - } - - fun video(message: VKMessage, attachments: LinearLayout) { - - } - - fun photo(message: VKMessage, attachments: LinearLayout) { - } - - fun audio(message: VKMessage, attachments: LinearLayout) { - - } - - fun doc(message: VKMessage, attachments: LinearLayout) { - - } - } - - inner class ViewController { - - fun prepareText(message: VKMessage, bubble: BoundedLinearLayout, text: TextView) { - val screenWidth = context.resources.displayMetrics.widthPixels - val boundedWidth = screenWidth - screenWidth / 5 - bubble.maxWidth = boundedWidth - - - - text.setTextColor( - AndroidUtils.getThemeAttrColor( - context, - if (message.isOutbox()) R.attr.messageOutTextColor else R.attr.messageInTextColor - ) - ) - text.text = message.text - } - - fun prepareDate(message: VKMessage, date: TextView) { - var dateText = - SimpleDateFormat("HH:mm", Locale.getDefault()).format(message.date * 1000L) - - if (message.editTime > 0) { - dateText += ", ${ - context.getString(R.string.edited) - .toLowerCase(Locale.getDefault()) - }" - } - - date.text = dateText - } - - fun prepareAvatar(message: VKMessage, avatar: ImageView) { - avatar.isVisible = !message.isOut - } - - fun loadAvatarImage(message: VKMessage, user: VKUser?, group: VKGroup?, avatar: ImageView) { -// val dialogTitle = VKUtil.getMessageTitle(message, user, group) -// val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, dialogTitle) -// -// avatar.setImageDrawable(avatarPlaceholder) -// -// val avatarString = VKUtil.getUserAvatar(message, user, group) -// -// ImageUtils.loadImage(avatarString, avatar, avatarPlaceholder) - } - - } - - open inner class Holder(v: View) : BaseHolder(v) { - override fun bind(position: Int) { - } - } - - inner class PlaceHolder(v: View) : NormalViewHolder(v) - - private fun searchUser(message: VKMessage): VKUser? { - if (!message.isFromUser()) return null - -// return VKUtil.searchUser(message.fromId) - return null - } - - private fun searchGroup(message: VKMessage): VKGroup? { - if (!message.isFromGroup()) return null - -// return VKUtil.searchGroup(message.fromId) - return null - } - - private fun updateGroup(groupIds: ArrayList) { - for (groupId in groupIds) { - var index = -1 - - for (i in values.indices) { - val item = getItem(i) - - if (abs(item.fromId) == groupId) { - index = i - break - } - } - - if (index == -1) return - - notifyItemChanged(index) - } - } - - private fun updateUser(userIds: ArrayList) { - for (userId in userIds) { - var index = -1 - - for (i in values.indices) { - val item = getItem(i) - - if (item.fromId == userId) { - index = i - break - } - } - - if (index == -1) return - notifyItemChanged(index) - } - } - - private fun updateMessage(messageId: Int) { - var index = -1 - - for (i in values.indices) { - val item = getItem(i) - - if (item.id == messageId) { - index = i - break - } - } - - if (index == -1) return - -// TaskManager.execute { -// AppGlobal.database.messages.getById(messageId)?.let { -// values[index] = it -// -// post { notifyItemChanged(index) } -// } -// } - } - - private fun searchMessagePosition(messageId: Int): Int { - for (i in values.indices) { - if (getItem(i).id == messageId) return i - } - - return -1 - } - - private fun containsRandomId(randomId: Int): Boolean { - for (message in values) { - if (message.randomId == randomId) return true - } - - return false - } - - fun addMessage(message: VKMessage, fromApp: Boolean = false, withScroll: Boolean = false) { - val randomId = message.randomId - if (randomId > 0 && containsRandomId(message.randomId) || message.peerId != conversation.id) return - - add(message) - - notifyDataSetChanged() - - val lastVisiblePosition = layoutManager.findLastVisibleItemPosition() - - if ((message.isInbox() && lastVisiblePosition >= itemCount - 2) || !fromApp || withScroll) { - recyclerView.scrollToPosition(itemCount - 1) - } - } - - private fun readMessage(peerId: Int, messageId: Int) { - if (peerId != conversation.id) return - - val index = searchMessagePosition(messageId) - if (index == -1) return - - val message = this[index] - message.isRead = true - - notifyDataSetChanged() - - if (message.isInbox()) { - conversation.inReadMessageId = messageId - } else { - conversation.outReadMessageId = messageId - } - - conversation.unreadCount-- - -// TaskManager.execute { -// MemoryCache.put(message) -// MemoryCache.put(conversation) -// } - } - - fun editMessage(message: VKMessage) { - val index = searchMessagePosition(message.id) - if (index == -1) return - - set(index, message) - notifyDataSetChanged() - } - - fun deleteMessage(messageId: Int, peerId: Int) { - if (peerId != conversation.id) return - - val index = searchMessagePosition(messageId) - if (index == -1) return - - removeAt(index) - notifyDataSetChanged() - } - - //TODO: кривое сообщение - fun restoreMessage(message: VKMessage) { - if (message.peerId != conversation.id) return - - updateValues(VKUtil.sortMessagesByDate(values.apply { add(message) }, false)) - notifyDataSetChanged() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/SimpleItemAdapter.kt b/app/src/main/java/com/meloda/fast/adapter/SimpleItemAdapter.kt deleted file mode 100644 index fabbb434..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/SimpleItemAdapter.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.meloda.fast.adapter - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import com.meloda.fast.R -import com.meloda.fast.base.BaseAdapter -import com.meloda.fast.base.BaseHolder -import com.meloda.fast.item.SimpleMenuItem -import java.util.* - -class SimpleItemAdapter(context: Context, values: ArrayList) : - BaseAdapter(context, values) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(view(R.layout.item_simple_menu, parent)) - } - - inner class ViewHolder(v: View) : BaseHolder(v) { - - private val title: TextView = v.findViewById(R.id.profileItemTitle) - private val icon: ImageView = v.findViewById(R.id.profileItemIcon) - - override fun bind(position: Int) { - val item = getItem(position) - - title.text = item.title - - icon.setImageDrawable(item.icon) - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/UsersAdapterDeprecated.kt b/app/src/main/java/com/meloda/fast/adapter/UsersAdapterDeprecated.kt deleted file mode 100644 index 20e3f03a..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/UsersAdapterDeprecated.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.meloda.fast.adapter - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil -import com.meloda.fast.R -import com.meloda.fast.adapter.diffutil.UsersCallbackDeprecated -import com.meloda.fast.base.BaseAdapter -import com.meloda.fast.base.BaseHolder -import com.meloda.fast.util.ImageUtils -import com.meloda.fast.widget.CircleImageView -import com.meloda.vksdk.model.VKUser -import com.meloda.vksdk.util.VKUtil - -class UsersAdapterDeprecated(context: Context, values: ArrayList) : - BaseAdapter(context, values) { - - var isLoading: Boolean = false - var currentPosition: Int = 0 - - fun isLastItem() = currentPosition >= itemCount - 1 - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(view(R.layout.item_user, parent)) - } - - open inner class ViewHolder(v: View) : BaseHolder(v) { - private val avatar: CircleImageView = v.findViewById(R.id.userAvatar) - private val name: TextView = v.findViewById(R.id.userName) - private val online: ImageView = v.findViewById(R.id.userOnline) - private val onlineText: TextView = v.findViewById(R.id.userOnlineText) - - override fun bind(position: Int) { - currentPosition = position - - val user = getItem(position) - - name.text = user.toString() - -// val avatarPlaceholder = VKUtil.getAvatarPlaceholder(context, user.toString()) -// avatar.setImageDrawable(avatarPlaceholder) - -// ImageUtils.loadImage(user.photo200, avatar, avatarPlaceholder) - - online.isVisible = false - -// VKUtil.getUserOnlineIcon(context, user)?.let { -// online.setImageDrawable(it) -// online.isVisible = true -// } - -// onlineText.text = VKUtil.getUserOnline(user) - - //TODO: отладить открытие чата - } - } - - override fun notifyChanges(oldList: List, newList: List) { - val callback = UsersCallbackDeprecated(oldList, newList) - val diff = DiffUtil.calculateDiff(callback, false) - - diff.dispatchUpdatesTo(this) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallback.kt b/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallback.kt deleted file mode 100644 index 6302806b..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallback.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.meloda.fast.adapter.diffutil - -import androidx.recyclerview.widget.DiffUtil -import com.meloda.vksdk.model.VKConversation - -class ConversationsCallback( - private val oldList: List, - private val newList: List -) : DiffUtil.Callback() { - - override fun getOldListSize() = oldList.size - - override fun getNewListSize() = newList.size - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - //TODO: rewrite - return false - } - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - //TODO: rewrite - return false - } - - override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { - //TODO: rewrite - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallbackDeprecated.kt b/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallbackDeprecated.kt deleted file mode 100644 index cb03d6cc..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/diffutil/ConversationsCallbackDeprecated.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.meloda.fast.adapter.diffutil - -import androidx.recyclerview.widget.DiffUtil -import com.meloda.vksdk.model.VKConversation - -class ConversationsCallbackDeprecated( - private val oldList: List, - private val newList: List -) : DiffUtil.Callback() { - - companion object { - const val DATE = "date" - const val ONLINE = "online" - const val AVATAR = "avatar" - const val USER_AVATAR = "user_avatar" - const val ATTACHMENTS = "attachments" - const val READ = "read" - const val NOTIFICATIONS = "notifications" - const val EDIT_MESSAGE = "edit_message" - const val MESSAGE = "message" - const val USER = "user" - const val GROUP = "group" - const val CONVERSATION = "conversation" - } - - override fun getOldListSize() = oldList.size - - override fun getNewListSize() = newList.size - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - val old = oldList[oldItemPosition] - val new = newList[newItemPosition] - - if (true) return false - return old.id == new.id - } - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - val old = oldList[oldItemPosition] - val new = newList[newItemPosition] - - val oldMessage = old.lastMessage - val newMessage = new.lastMessage - - if (true) return false else - - return old.title == new.title && - old.lastMessageId == new.lastMessageId && - old.photo50 == new.photo50 && - old.unreadCount == new.unreadCount && - - old.isNoSound == new.isNoSound && - old.isDisabledForever == new.isDisabledForever && - old.disabledUntil == new.disabledUntil && - - old.inReadMessageId == new.inReadMessageId && - old.outReadMessageId == new.outReadMessageId && - - old.peerUser == new.peerUser && - old.peerGroup == new.peerGroup && - - oldMessage == newMessage - -// oldMessage.messageId == newMessage.messageId && -// oldMessage.isOut == newMessage.isOut && -// oldMessage.fromId == newMessage.fromId && -// oldMessage.date == newMessage.date && -// oldMessage.action == newMessage.action && -// oldMessage.text == newMessage.text && -// oldMessage.attachments == newMessage.attachments && -// oldMessage.fwdMessages == newMessage.fwdMessages && -// oldMessage.messageId == newMessage.messageId && -// -// oldMessage.fromUser == newMessage.fromUser && -// oldMessage.fromGroup == newMessage.fromGroup - } - - override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { - val oldConversation = oldList[oldItemPosition] - val newConversation = newList[newItemPosition] - - val oldMessage = oldConversation.lastMessage - val newMessage = newConversation.lastMessage - - val oldDate = oldMessage.date - val newDate = newMessage.date - -// if (oldDate != newDate) return DATE - -// if (oldMessage != newMessage) return MESSAGE - - return super.getChangePayload(oldItemPosition, newItemPosition) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/adapter/diffutil/UsersCallbackDeprecated.kt b/app/src/main/java/com/meloda/fast/adapter/diffutil/UsersCallbackDeprecated.kt deleted file mode 100644 index 11878b0b..00000000 --- a/app/src/main/java/com/meloda/fast/adapter/diffutil/UsersCallbackDeprecated.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.meloda.fast.adapter.diffutil - -import androidx.recyclerview.widget.DiffUtil -import com.meloda.vksdk.model.VKUser - -class UsersCallbackDeprecated(private val oldList: List, private val newList: List) : - DiffUtil.Callback() { - - companion object { - const val ONLINE = "online" - const val ONLINE_MOBILE = "online_mobile" - } - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - val old = oldList[oldItemPosition] - val new = newList[newItemPosition] - - return old.userId == new.userId - } - - override fun getOldListSize() = oldList.size - - override fun getNewListSize() = newList.size - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - val old = oldList[oldItemPosition] - val new = newList[newItemPosition] - - return old.firstName == new.firstName && - old.lastName == new.lastName && - old.isOnline == new.isOnline && - old.isOnlineMobile == new.isOnlineMobile && - old.lastSeen == new.lastSeen && - old.lastSeenPlatform == new.lastSeenPlatform && - old.deactivated == new.deactivated - } - - override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { - val old = oldList[oldItemPosition] - val new = newList[newItemPosition] - - if (old.isOnlineMobile != new.isOnlineMobile) { - if (old.isOnline != new.isOnline) return ONLINE - - return ONLINE_MOBILE - } - - return super.getChangePayload(oldItemPosition, newItemPosition) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/BaseActivity.kt b/app/src/main/java/com/meloda/fast/base/BaseActivity.kt deleted file mode 100644 index 4e800f3d..00000000 --- a/app/src/main/java/com/meloda/fast/base/BaseActivity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.meloda.fast.base - -import android.os.Build -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import com.meloda.extensions.ContextExtensions.color -import com.meloda.fast.R -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.util.ColorUtils - -abstract class BaseActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { - val navigationBarColor = - if (AndroidUtils.isDarkTheme()) { - color(R.color.dark_primaryDark) - } else { - ColorUtils.darkenColor(color(R.color.primaryDark)) - } - - window.navigationBarColor = navigationBarColor - } - } - - fun getRootView(): View { - return findViewById(android.R.id.content) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/BaseAdapter.kt b/app/src/main/java/com/meloda/fast/base/BaseAdapter.kt deleted file mode 100644 index e5982b94..00000000 --- a/app/src/main/java/com/meloda/fast/base/BaseAdapter.kt +++ /dev/null @@ -1,177 +0,0 @@ -package com.meloda.fast.base - -import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import androidx.recyclerview.widget.RecyclerView -import com.meloda.arrayutils.ArrayUtils.asArrayList -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.listener.ItemLongClickListener -import java.io.Serializable -import java.util.* - - -@Suppress("UNCHECKED_CAST") -abstract class BaseAdapter( - var context: Context, - var values: ArrayList = arrayListOf() -) : RecyclerView.Adapter() { - - companion object { - private const val P_ITEMS = "BaseAdapter.values" - } - - private var cleanValues: ArrayList? = null - - private var inflater: LayoutInflater = LayoutInflater.from(context) - - var itemClickListener: ItemClickListener? = null - var itemLongClickListener: ItemLongClickListener? = null - - open fun destroy() {} - - open fun getItem(position: Int): Item { - return values[position] - } - - fun add(position: Int, item: Item) { - values.add(position, item) - cleanValues?.add(position, item) - } - - fun add(item: Item) { - values.add(item) - cleanValues?.add(item) - } - - fun addAll(items: List) { - values.addAll(items) - cleanValues?.addAll(items) - } - - fun addAll(position: Int, items: List) { - values.addAll(position, items) - cleanValues?.addAll(position, items) - } - - operator fun set(position: Int, item: Item) { - values[position] = item - cleanValues?.set(position, item) - } - - fun indexOf(item: Item): Int { - return values.indexOf(item) - } - - fun removeAt(index: Int) { - values.removeAt(index) - cleanValues?.removeAt(index) - } - - fun remove(item: Item) { - values.remove(item) - cleanValues?.remove(item) - } - - open fun notifyChanges(oldList: List, newList: List = values) {} - - fun isEmpty() = values.isNullOrEmpty() - - fun isNotEmpty() = !isEmpty() - - fun view(resId: Int, viewGroup: ViewGroup): View { - return inflater.inflate(resId, viewGroup, false) - } - - fun updateValues(arrayList: ArrayList) { - values.clear() - values.addAll(arrayList) - } - - fun updateValues(list: List) = updateValues(list.asArrayList()) - - override fun onBindViewHolder(holder: VH, position: Int) { - onBindItemViewHolder(holder, position) - } - - protected fun initListeners(itemView: View, position: Int) { - if (itemView is AdapterView<*>) return - - itemView.setOnClickListener { - if (itemClickListener != null) itemClickListener!!.onItemClick( - position - ) - } - itemView.setOnLongClickListener { - if (itemLongClickListener != null) itemLongClickListener!!.onItemLongClick(position) - itemClickListener == null - } - } - - override fun getItemCount(): Int { - return values.size - } - - private fun onBindItemViewHolder(holder: VH, position: Int) { - initListeners(holder.itemView, position) - holder.bind(position) - } - - fun onSaveInstanceState(): Parcelable { - val bundle = Bundle() - if (values.size > 0 && (values[0] is Parcelable || values[0] is Serializable)) { - bundle.putSerializable(P_ITEMS, values) - } - return bundle - } - - fun post(runnable: Runnable) { - AppGlobal.handler.post(runnable) - } - - fun onRestoreInstanceState(state: Parcelable?) { - if (state is Bundle) { - if (state.containsKey(P_ITEMS)) { - values = state.getSerializable(P_ITEMS) as ArrayList - } - } - } - - fun clear() { - values.clear() - } - - open fun filter(query: String) { - if (cleanValues == null) { - cleanValues = ArrayList(values) - } - - values.clear() - - if (query.isEmpty()) { - values.addAll(cleanValues!!) - } else { - for (item in cleanValues!!) { - if (onQueryItem(item, query)) { - values.add(item) - } - } - } - - notifyDataSetChanged() - } - - open fun onQueryItem(item: Item, query: String): Boolean { - return false - } - - operator fun get(index: Int): Item { - return values[index] - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/BaseFragment.kt b/app/src/main/java/com/meloda/fast/base/BaseFragment.kt deleted file mode 100644 index dc5159b2..00000000 --- a/app/src/main/java/com/meloda/fast/base/BaseFragment.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.meloda.fast.base - -import androidx.annotation.IdRes -import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.Fragment -import com.meloda.fast.activity.MainActivity - -abstract class BaseFragment : Fragment() { - - protected open fun initToolbar(@IdRes resId: Int) { - val toolbar: Toolbar = requireView().findViewById(resId) - - activity?.let { - if (it is MainActivity && toolbar is com.meloda.fast.widget.Toolbar) it.initToolbar( - toolbar - ) - } - } - - fun runOnUiThread(runnable: Runnable) { - activity?.runOnUiThread(runnable) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/BaseHolder.kt b/app/src/main/java/com/meloda/fast/base/BaseHolder.kt deleted file mode 100644 index 563da0a7..00000000 --- a/app/src/main/java/com/meloda/fast/base/BaseHolder.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.meloda.fast.base - -import android.view.View -import androidx.recyclerview.widget.RecyclerView - -abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) { - - open fun bind(position: Int) { - bind(position, mutableListOf()) - } - - open fun bind(position: Int, payloads: MutableList?) {} - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/dialog/AccountDialog.kt b/app/src/main/java/com/meloda/fast/dialog/AccountDialog.kt deleted file mode 100644 index 4c299bd6..00000000 --- a/app/src/main/java/com/meloda/fast/dialog/AccountDialog.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.meloda.fast.dialog - -import android.content.Intent -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.RelativeLayout -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.facebook.imagepipeline.cache.MemoryCache -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.activity.SettingsActivityDeprecated -import com.meloda.fast.adapter.SimpleItemAdapter -import com.meloda.fast.base.BaseFullscreenDialog -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.item.SimpleMenuItem -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.util.ColorUtils -import com.meloda.fast.util.ViewUtils -import com.meloda.fast.widget.Toolbar - -class AccountDialog : BaseFullscreenDialog(), ItemClickListener { - - companion object { - const val TAG = "account_fullscreen_dialog" - } - - private lateinit var adapter: SimpleItemAdapter - - private lateinit var toolbar: Toolbar - private lateinit var recyclerView: RecyclerView - private lateinit var refreshLayout: SwipeRefreshLayout - private lateinit var headerRoot: RelativeLayout - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.dialog_account, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - initViews() - prepareToolbar() - prepareRecyclerView() - } - - private fun initViews() { - toolbar = requireView().findViewById(R.id.toolbar) - recyclerView = requireView().findViewById(R.id.recyclerView) - refreshLayout = requireView().findViewById(R.id.refreshLayout) - headerRoot = requireView().findViewById(R.id.headerRoot) - } - - private fun prepareToolbar() { - toolbar.navigationIcon = requireContext().drawable(R.drawable.ic_close) - toolbar.tintNavigationIcon(ColorUtils.getColorAccent(requireContext())) - - toolbar.setTitle(R.string.account_dialog_title) - toolbar.setTitleMode(Toolbar.TitleMode.SIMPLE) - toolbar.setNavigationClickListener { dismiss() } - -// MemoryCache.getUserById(UserConfig.userId)?.let { -// AppGlobal.handler.post { ViewUtils.prepareNavigationHeader(headerRoot, it) } -// } - } - - private fun prepareRecyclerView() { - refreshLayout.isEnabled = false - - recyclerView.layoutManager = - LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - recyclerView.setHasFixedSize(true) - - createItemsAndAdapter() - } - - private fun createItemsAndAdapter() { - val items = arrayListOf() - - SimpleMenuItem( - requireContext().drawable(R.drawable.ic_settings_outline) - .tint(ColorUtils.getColorAccent(requireContext())), - requireContext().getString(R.string.navigation_settings) - ) { openSettingsScreen() }.let { items.add(it) } - - adapter = SimpleItemAdapter(requireContext(), items).also { - it.itemClickListener = this - } - - recyclerView.adapter = adapter - } - - private fun openSettingsScreen() { - startActivity(Intent(requireContext(), SettingsActivityDeprecated::class.java)) - } - - override fun onItemClick(position: Int) { - val item = adapter.getItem(position) - - item.clickListener?.let { - it.onClick(requireView().findViewById(android.R.id.content)) - dismiss() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/dialog/ProfileDialog.kt b/app/src/main/java/com/meloda/fast/dialog/ProfileDialog.kt deleted file mode 100644 index b7c616a9..00000000 --- a/app/src/main/java/com/meloda/fast/dialog/ProfileDialog.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.meloda.fast.dialog - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.meloda.fast.R -import com.meloda.fast.adapter.SimpleItemAdapter -import com.meloda.fast.item.SimpleMenuItem -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKUser - -open class ProfileDialog( - private val conversation: VKConversation, - private val chatTitle: String -) : BottomSheetDialogFragment() { - - companion object { - const val TAG = "profile_bottom_sheet_dialog" - } - - private lateinit var title: TextView - private lateinit var subtitle: TextView - private lateinit var recyclerView: RecyclerView - private lateinit var root: LinearLayout - - private var adapter: SimpleItemAdapter? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setStyle(STYLE_NO_TITLE, R.style.AppTheme_ProfileDialog) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.dialog_profile_bottom, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - title = view.findViewById(R.id.profileTitle) - subtitle = view.findViewById(R.id.profileSubtitle) - recyclerView = view.findViewById(R.id.profileItemMenu) - root = view.findViewById(R.id.profileRoot) - - val layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - - recyclerView.layoutManager = layoutManager - - title.text = chatTitle - - subtitle.text = getSubtitle() - - val items = ArrayList() - - items.add( - SimpleMenuItem( - ContextCompat.getDrawable( - requireContext(), - R.drawable.ic_search - )!!, "Search" - ) - ) - - createAdapter(items) - } - - private fun createAdapter(items: ArrayList) { - adapter = SimpleItemAdapter(requireContext(), items) - recyclerView.adapter = adapter - } - - private fun getSubtitle(): String { - return when (conversation.type) { - VKConversation.Type.CHAT -> getString( - R.string.chat_members, - conversation.membersCount - ) - VKConversation.Type.GROUP -> { -// val group = MemoryCache.getGroupById(conversation.conversationId) ?: return "" -// -// "@${group.screenName}" - "" - } - VKConversation.Type.USER -> { -// val user = MemoryCache.getUserById(conversation.id) ?: return "" - - //TODO: придумать чо делать - val user: VKUser = null ?: return "" - - var str = - if (user.screenName.contains("id${user.userId}")) "" else "@${user.screenName}" - - val online = - getString(if (user.isOnlineMobile) R.string.user_online_mobile else if (user.isOnline) R.string.user_online else R.string.user_offline) - - str += if (str.isEmpty()) online else " · $online" - - str - } - else -> "" - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ChatsFragment.kt b/app/src/main/java/com/meloda/fast/fragment/ChatsFragment.kt deleted file mode 100644 index d3b0582c..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ChatsFragment.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.meloda.fast.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.ProgressBar -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.adapter.ConversationsAdapterDeprecated -import com.meloda.fast.base.BaseFragment -import com.meloda.fast.database.CacheStorage -import com.meloda.fast.widget.Toolbar -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.VKConstants -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage - -class ChatsFragment : BaseFragment() { - - private lateinit var toolbar: Toolbar - private lateinit var progressBar: ProgressBar - private lateinit var recyclerView: RecyclerView - private lateinit var refreshLayout: SwipeRefreshLayout - - private lateinit var noItemsView: LinearLayout - private lateinit var noInternetView: LinearLayout - private lateinit var errorView: LinearLayout - - private lateinit var adapter: ConversationsAdapterDeprecated - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_conversations, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - initViews() - prepareViews() - - createAdapter() - - loadConversations() - } - - private fun initViews() { - toolbar = requireView().findViewById(R.id.toolbar) - progressBar = requireView().findViewById(R.id.progressBar) - recyclerView = requireView().findViewById(R.id.recyclerView) - refreshLayout = requireView().findViewById(R.id.refreshLayout) - - noItemsView = requireView().findViewById(R.id.noItemsView) - noInternetView = requireView().findViewById(R.id.noInternetView) - errorView = requireView().findViewById(R.id.errorView) - } - - private fun prepareViews() { - prepareRecyclerView() - } - - private fun prepareRecyclerView() { - val manager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - - recyclerView.layoutManager = manager - } - - private fun createAdapter() { - adapter = ConversationsAdapterDeprecated(recyclerView, arrayListOf()) - recyclerView.adapter = adapter - } - - private fun loadConversations() { - TaskManager.execute { - VKApi.messages() - .getConversations() - .filter("all") - .extended(true) - .fields(VKConstants.USER_FIELDS) - .offset(0) - .count(30) - .executeArray( - VKConversation::class.java, - object : OnResponseListener> { - override fun onResponse(response: ArrayList) { - TaskManager.execute { - CacheStorage.chatsStorage.insertValues(response) - - val lastMessages = arrayListOf() - response.forEach { lastMessages.add(it.lastMessage) } - - CacheStorage.messagesStorage.insertValues(lastMessages) - CacheStorage.usersStorage.insertValues(VKConversation.profiles) - CacheStorage.groupsStorage.insertValues(VKConversation.groups) - } - - adapter.updateValues(response) - adapter.notifyDataSetChanged() - } - - override fun onError(t: Throwable) { - - } - }) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/FragmentConversationsDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/FragmentConversationsDeprecated.kt deleted file mode 100644 index e93d1243..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/FragmentConversationsDeprecated.kt +++ /dev/null @@ -1,186 +0,0 @@ -package com.meloda.fast.fragment - -import android.content.Intent -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.ProgressBar -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.activity.MessagesActivityDeprecated -import com.meloda.fast.base.BaseFragment -import com.meloda.fast.fragment.ui.presenter.ConversationsPresenterDeprecated -import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.util.ViewUtils -import com.meloda.fast.widget.Toolbar -import com.meloda.vksdk.VKApiKeys - -@Suppress("UNCHECKED_CAST") -class FragmentConversationsDeprecated : BaseFragment(), ConversationsViewDeprecated { - - private lateinit var presenterDeprecated: ConversationsPresenterDeprecated - - private lateinit var toolbar: Toolbar - private lateinit var refreshLayout: SwipeRefreshLayout - private lateinit var recyclerView: RecyclerView - private lateinit var progressBar: ProgressBar - - private lateinit var noItemsView: LinearLayout - private lateinit var noInternetView: LinearLayout - private lateinit var errorView: LinearLayout - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? = inflater.inflate(R.layout.fragment_conversations, container, false) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - initViews() - - prepareToolbar() - prepareRecyclerView() - prepareRefreshLayout() - - presenterDeprecated = ConversationsPresenterDeprecated(this) - presenterDeprecated.setup(recyclerView, refreshLayout) - } - - private fun initViews() { - toolbar = requireView().findViewById(R.id.toolbar) - recyclerView = requireView().findViewById(R.id.recyclerView) - refreshLayout = requireView().findViewById(R.id.refreshLayout) - progressBar = requireView().findViewById(R.id.progressBar) - - noItemsView = requireView().findViewById(R.id.noItemsView) - noInternetView = requireView().findViewById(R.id.noInternetView) - errorView = requireView().findViewById(R.id.errorView) - } - - private fun prepareToolbar() { - initToolbar(R.id.toolbar) - toolbar.title = getString(R.string.navigation_chats) - setProfileAvatar() - - TaskManager.addOnEventListener(object : TaskManager.OnEventListener { - override fun onNewEvent(info: EventInfo<*>) { - if (info.key == VKApiKeys.UPDATE_USER.name) { - val userIds = info.data as ArrayList - - if (userIds.contains(UserConfig.userId)) { - setProfileAvatar() - } - } - } - }) - } - - private fun prepareRefreshLayout() { - refreshLayout.setColorSchemeResources(R.color.accent) - } - - private fun prepareRecyclerView() { - val manager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - - val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) - - decoration.setDrawable( - ColorDrawable( - AndroidUtils.getThemeAttrColor( - requireContext(), - R.attr.dividerHorizontal - ) - ) - ) - - recyclerView.itemAnimator = null - recyclerView.addItemDecoration(decoration) - - recyclerView.layoutManager = manager - } - - private fun setProfileAvatar() { - TaskManager.execute { -// AppGlobal.database.users.getById(UserConfig.userId)?.let { -// if (it.photo100.isNotEmpty()) { -// runOnUiThread { -// toolbar.getAvatar().setImageURI(it.photo100) -// } -// } -// } - } - } - - override fun openChat(extras: Bundle) { - startActivity( - Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras( - extras - ) - ) - } - - override fun showErrorSnackbar(t: Throwable) { - ViewUtils.showErrorSnackbar(requireView(), t) - } - - override fun prepareNoItemsView() { - } - - override fun showNoItemsView() { - noItemsView.isVisible = true - } - - override fun hideNoItemsView() { - noItemsView.isVisible = false - } - - override fun prepareNoInternetView() { - } - - override fun showNoInternetView() { - noInternetView.isVisible = true - } - - override fun hideNoInternetView() { - noInternetView.isVisible = false - } - - override fun prepareErrorView() { - - } - - override fun showErrorView() { - errorView.isVisible = true - } - - override fun hideErrorView() { - errorView.isVisible = false - } - - override fun showProgressBar() { - progressBar.isVisible = true - } - - override fun hideProgressBar() { - progressBar.isVisible = false - } - - override fun showRefreshLayout() { - refreshLayout.isRefreshing = true - } - - override fun hideRefreshLayout() { - refreshLayout.isRefreshing = false - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/FragmentFriendsDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/FragmentFriendsDeprecated.kt deleted file mode 100644 index d4960583..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/FragmentFriendsDeprecated.kt +++ /dev/null @@ -1,194 +0,0 @@ -package com.meloda.fast.fragment - -import android.content.Intent -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.ProgressBar -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.activity.MessagesActivityDeprecated -import com.meloda.fast.base.BaseFragment -import com.meloda.fast.fragment.ui.presenter.FriendsPresenterDeprecated -import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated -import com.meloda.fast.util.ViewUtils -import com.meloda.fast.widget.Toolbar -import com.meloda.vksdk.VKApiKeys - -class FragmentFriendsDeprecated(private val userId: Int = 0) : BaseFragment(), - FriendsViewDeprecated { - - private lateinit var presenterDeprecated: FriendsPresenterDeprecated - - private lateinit var toolbar: Toolbar - private lateinit var recyclerView: RecyclerView - private lateinit var refreshLayout: SwipeRefreshLayout - private lateinit var progressBar: ProgressBar - - private lateinit var noItemsView: LinearLayout - private lateinit var noInternetView: LinearLayout - private lateinit var errorView: LinearLayout - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_friends, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - initViews() - - prepareToolbar() - prepareRecyclerView() - prepareRefreshLayout() - - presenterDeprecated = FriendsPresenterDeprecated(this) - presenterDeprecated.setup(userId, recyclerView, refreshLayout) - } - - private fun initViews() { - toolbar = requireView().findViewById(R.id.toolbar) - recyclerView = requireView().findViewById(R.id.recyclerView) - refreshLayout = requireView().findViewById(R.id.refreshLayout) - progressBar = requireView().findViewById(R.id.progressBar) - - noItemsView = requireView().findViewById(R.id.noItemsView) - noInternetView = requireView().findViewById(R.id.noInternetView) - errorView = requireView().findViewById(R.id.errorView) - } - - private fun prepareToolbar() { - initToolbar(R.id.toolbar) - toolbar.title = getString(R.string.navigation_friends) - setProfileAvatar() - - toolbar.inflateMenu(R.menu.fragment_friends) - - TaskManager.addOnEventListener(object : TaskManager.OnEventListener { - override fun onNewEvent(info: EventInfo<*>) { - if (info.key == VKApiKeys.UPDATE_USER.name) { - val userId = info.data as ArrayList - - if (userId[0] == UserConfig.userId) { - setProfileAvatar() - } - } - } - }) - } - - private fun setProfileAvatar() { - TaskManager.execute { -// AppGlobal.database.users.getById(UserConfig.userId)?.let { -// if (it.photo100.isNotEmpty()) { -// runOnUi { -// toolbar.getAvatar().setImageURI(it.photo100) -// } -// } -// } - } - } - - override fun onDetach() { - presenterDeprecated.destroy() - super.onDetach() - } - - private fun prepareRefreshLayout() { - refreshLayout.setColorSchemeResources(R.color.accent) - } - - private fun prepareRecyclerView() { - val manager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - - val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) - - decoration.setDrawable( - ColorDrawable( - ContextCompat.getColor( - requireContext(), - R.color.divider - ) - ) - ) - - recyclerView.addItemDecoration(decoration) - recyclerView.layoutManager = manager - } - - override fun openChat(extras: Bundle) { - startActivity( - Intent(requireContext(), MessagesActivityDeprecated::class.java).putExtras( - extras - ) - ) - } - - override fun showErrorSnackbar(t: Throwable) { - ViewUtils.showErrorSnackbar(requireView(), t) - } - - override fun prepareNoItemsView() { - } - - override fun showNoItemsView() { - noItemsView.isVisible = true - } - - override fun hideNoItemsView() { - noItemsView.isVisible = false - } - - override fun prepareNoInternetView() { - } - - override fun showNoInternetView() { - noInternetView.isVisible = true - } - - override fun hideNoInternetView() { - noInternetView.isVisible = false - } - - override fun prepareErrorView() { - } - - override fun showErrorView() { - errorView.isVisible = true - } - - override fun hideErrorView() { - errorView.isVisible = false - } - - - override fun showProgressBar() { - progressBar.isVisible = true - } - - override fun hideProgressBar() { - progressBar.isVisible = false - } - - override fun showRefreshLayout() { - refreshLayout.isRefreshing = true - } - - override fun hideRefreshLayout() { - refreshLayout.isRefreshing = false - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/LoginFragment.kt b/app/src/main/java/com/meloda/fast/fragment/LoginFragment.kt deleted file mode 100644 index 72a6530e..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/LoginFragment.kt +++ /dev/null @@ -1,171 +0,0 @@ -package com.meloda.fast.fragment - -import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import com.google.android.material.button.MaterialButton -import com.google.android.material.card.MaterialCardView -import com.meloda.fast.R -import com.meloda.fast.base.BaseFragment -import com.meloda.fast.fragment.ui.presenter.LoginPresenter -import com.meloda.fast.fragment.ui.view.LoginView -import com.meloda.fast.util.AndroidUtils -import com.meloda.fast.util.KeyboardUtils -import kotlin.math.roundToInt - -class LoginFragment : BaseFragment(), LoginView { - - private lateinit var presenter: LoginPresenter - - private lateinit var email: EditText - private lateinit var password: EditText - private lateinit var authorize: MaterialButton - private lateinit var card: MaterialCardView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - presenter = LoginPresenter(this) - presenter.onCreate(requireContext(), this, savedInstanceState) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - presenter.onCreateView(savedInstanceState) - return inflater.inflate(R.layout.fragment_login, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - presenter.onViewCreated(savedInstanceState) - } - - override fun initViews() { - email = requireView().findViewById(R.id.loginEmailEditText) - password = requireView().findViewById(R.id.loginPasswordEditText) - authorize = requireView().findViewById(R.id.loginAuthorize) - card = requireView().findViewById(R.id.loginCard) - } - - override fun prepareViews() { - prepareEmailEditText() - preparePasswordEditText() - prepareAuthorizeButton() - prepareCardView() - } - - private fun prepareEmailEditText() { - email.addTextChangedListener(onTextChangedListener) - } - - private fun preparePasswordEditText() { - password.addTextChangedListener(onTextChangedListener) - - password.setOnEditorActionListener { _, _, event -> - if (event == null) return@setOnEditorActionListener false - return@setOnEditorActionListener if (event.action == EditorInfo.IME_ACTION_GO || - (event.action == KeyEvent.ACTION_DOWN && (event.keyCode == KeyEvent.KEYCODE_ENTER || event.keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER)) - ) { - KeyboardUtils.hideKeyboardFrom(password) - authorize.performClick() - true - } else false - } - } - - private fun prepareAuthorizeButton() { - authorize.isEnabled = false - authorize.setOnClickListener { - val emailString = email.text.toString().trim() - val passwordString = password.text.toString().trim() - - presenter.login(emailString, passwordString) - } - } - - private fun prepareCardView() { - val width = AndroidUtils.dp(resources.displayMetrics.widthPixels).roundToInt() - - if (width < 380) { - card.strokeWidth = 0 - } - } - - override fun showErrorSnackbar(t: Throwable) { - TODO("Not yet implemented") - } - - override fun prepareNoItemsView() { - TODO("Not yet implemented") - } - - override fun showNoItemsView() { - TODO("Not yet implemented") - } - - override fun hideNoItemsView() { - TODO("Not yet implemented") - } - - override fun prepareNoInternetView() { - TODO("Not yet implemented") - } - - override fun showNoInternetView() { - TODO("Not yet implemented") - } - - override fun hideNoInternetView() { - TODO("Not yet implemented") - } - - override fun prepareErrorView() { - TODO("Not yet implemented") - } - - override fun showErrorView() { - TODO("Not yet implemented") - } - - override fun hideErrorView() { - TODO("Not yet implemented") - } - - override fun showProgressBar() { - TODO("Not yet implemented") - } - - override fun hideProgressBar() { - TODO("Not yet implemented") - } - - override fun showRefreshLayout() { - TODO("Not yet implemented") - } - - override fun hideRefreshLayout() { - TODO("Not yet implemented") - } - - private val onTextChangedListener = object : TextWatcher { - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - authorize.isEnabled = - email.text.toString().trim().isNotEmpty() && - password.text.toString().trim().isNotEmpty() - } - - override fun afterTextChanged(s: Editable?) { - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/SettingsFragment.kt b/app/src/main/java/com/meloda/fast/fragment/SettingsFragment.kt deleted file mode 100644 index 97220676..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/SettingsFragment.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.meloda.fast.fragment - -import android.content.Intent -import android.os.Bundle -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceScreen -import com.meloda.concurrent.TaskManager -import com.meloda.extensions.ContextExtensions.color -import com.meloda.fast.R -import com.meloda.fast.activity.DropUserDataActivity -import com.meloda.fast.activity.UpdateActivityDeprecated -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.util.AndroidUtils - -class SettingsFragment : PreferenceFragmentCompat(), - Preference.OnPreferenceClickListener, - Preference.OnPreferenceChangeListener { - - companion object { - - const val CATEGORY_GENERAL = "general" - const val KEY_HIDE_KEYBOARD_ON_SCROLL_UP = "hide_keyboard_on_scroll_up" - - const val CATEGORY_APPEARANCE = "appearance" - const val KEY_EXTENDED_CONVERSATIONS = "appearance_extended_conversations" - const val KEY_THEME = "appearance_theme" - - const val CATEGORY_ABOUT = "about" - const val KEY_APP_VERSION = "app_version" - - const val CATEGORY_ACCOUNT = "account" - const val KEY_ACCOUNT_LOGOUT = "account_logout" - - const val CATEGORY_DEBUG = "debug" - const val KEY_CLEAR_USERS_GROUPS_CACHE = "clear_users_groups_cache" - } - - private var currentPreferenceLayout = 0 - private var isRestoringState = false - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - setPreferencesFromResource(R.xml.fragment_settings, rootKey) - currentPreferenceLayout = R.xml.fragment_settings - init() - } - - private fun init() { - setTitle() - setPreferencesFromResource(currentPreferenceLayout, null) - - val general = findPreference(CATEGORY_GENERAL) - general?.onPreferenceClickListener = rootLayoutClickListener - - val account = findPreference(CATEGORY_ACCOUNT) - account?.onPreferenceClickListener = rootLayoutClickListener - - val logout = findPreference(KEY_ACCOUNT_LOGOUT) - logout?.onPreferenceClickListener = this - - val about = findPreference(CATEGORY_ABOUT) - about?.onPreferenceClickListener = rootLayoutClickListener - - val appVersion = findPreference(KEY_APP_VERSION) - appVersion?.onPreferenceClickListener = this - - val appearance = findPreference(CATEGORY_APPEARANCE) - appearance?.onPreferenceClickListener = rootLayoutClickListener - - val extendedConversations = findPreference(KEY_EXTENDED_CONVERSATIONS) - extendedConversations?.onPreferenceChangeListener = this - - val theme = findPreference(KEY_THEME) - theme?.onPreferenceChangeListener = this - - val debug = findPreference(CATEGORY_DEBUG) - debug?.onPreferenceClickListener = rootLayoutClickListener - updateDebugCategoryVisibility() - - val clearUsersGroupsCache = findPreference(KEY_CLEAR_USERS_GROUPS_CACHE) - clearUsersGroupsCache?.onPreferenceClickListener = this - - applyTintInPreferenceScreen(preferenceScreen) - } - - override fun onResume() { - super.onResume() - - updateDebugCategoryVisibility() - } - - private fun updateDebugCategoryVisibility() { - findPreference(CATEGORY_DEBUG)?.isVisible = - AndroidUtils.isDeveloperSettingsEnabled(requireContext()) - } - - private val rootLayoutClickListener = - Preference.OnPreferenceClickListener { changeRootLayout(it) } - - private fun setTitle() { - var title = R.string.navigation_settings - when (currentPreferenceLayout) { - R.xml.fragment_settings_general -> title = R.string.prefs_general - R.xml.fragment_settings_appearance -> title = R.string.prefs_appearance - R.xml.fragment_settings_about -> title = R.string.prefs_about - R.xml.fragment_settings_account -> title = R.string.prefs_account - } - requireActivity().setTitle(title) - } - - private fun changeRootLayout(preference: Preference): Boolean { - currentPreferenceLayout = when (preference.key) { - CATEGORY_GENERAL -> R.xml.fragment_settings_general - CATEGORY_ABOUT -> R.xml.fragment_settings_about - CATEGORY_ACCOUNT -> R.xml.fragment_settings_account - CATEGORY_APPEARANCE -> R.xml.fragment_settings_appearance - CATEGORY_DEBUG -> R.xml.fragment_settings_debug - else -> R.xml.fragment_settings - } - - init() - return true - } - - private fun applyTintInPreferenceScreen(rootScreen: PreferenceScreen) { - if (rootScreen.preferenceCount > 0) { - for (i in 0 until rootScreen.preferenceCount) { - val preference = rootScreen.getPreference(i) - tintPreference(preference) - } - } - } - - private fun tintPreference(preference: Preference) { - if (preference.icon != null && context != null) { - preference.icon.setTint(requireContext().color(R.color.accent)) - } - } - - override fun onPreferenceClick(preference: Preference): Boolean { - when (preference.key) { - KEY_ACCOUNT_LOGOUT -> { - logout() - return true - } - KEY_APP_VERSION -> { - openUpdateScreen() - return true - } - KEY_CLEAR_USERS_GROUPS_CACHE -> { - showClearCacheConfirmation() - } - } - return false - } - - private fun showClearCacheConfirmation() { - val builder = AlertDialog.Builder(requireContext()) - - builder.setMessage("Clear cache?") - builder.setPositiveButton("Yes") { _, _ -> - TaskManager.execute { -// AppGlobal.database.users.clear() -// AppGlobal.database.groups.clear() - } - } - builder.setNegativeButton("No", null) - builder.show() - } - - private fun openUpdateScreen() { - startActivity(Intent(requireContext(), UpdateActivityDeprecated::class.java)) - } - - override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { - when (preference.key) { - KEY_EXTENDED_CONVERSATIONS -> { - return true - } - KEY_THEME -> { - val nightMode = AppGlobal.instance.applyNightMode(newValue as String) - (requireActivity() as AppCompatActivity).delegate.localNightMode = nightMode - return true - } - } - - return false - } - - fun onBackPressed() = if (currentPreferenceLayout == R.xml.fragment_settings) { - true - } else { - currentPreferenceLayout = R.xml.fragment_settings - init() - false - } - - private fun logout() { - startActivity(Intent(requireContext(), DropUserDataActivity::class.java)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ValidationFragment.kt b/app/src/main/java/com/meloda/fast/fragment/ValidationFragment.kt deleted file mode 100644 index cb6aa28c..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ValidationFragment.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.meloda.fast.fragment - -import android.graphics.Bitmap -import android.os.Bundle -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.webkit.CookieManager -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.core.os.bundleOf -import com.meloda.fast.base.BaseFragment -import com.meloda.vksdk.VKAuth - - -class ValidationFragment : BaseFragment() { - - private var url: String = "" - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (arguments != null && requireArguments().isEmpty.not()) { - url = requireArguments().getString("url") ?: "" - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val webView = WebView(requireContext()) - webView.webViewClient = object : WebViewClient() { - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - parseUrl(url ?: "") - } - } - - webView.settings.domStorageEnabled = true - webView.clearCache(true) - webView.layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - - val manager = CookieManager.getInstance() - manager.removeAllCookies(null) - manager.flush() - manager.setAcceptCookie(true) - - return webView - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - (requireView() as WebView).loadUrl(url) - } - - private fun parseUrl(url: String) { - Log.d("WebView url", url) - try { - if (url.startsWith("https://oauth.vk.com/blank.html#success=1")) { - Log.d("Success WebView", "") - if (!url.contains("error=")) { - val auth = VKAuth.parseRedirectUrl(url) - - val token = auth[0] - val userId = auth[1].toInt() - - parentFragmentManager.setFragmentResult( - "validation", - bundleOf( - Pair("token", token), - Pair("userId", userId) - ) - ) - - parentFragmentManager.popBackStack() - - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ChatsPresenter.kt b/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ChatsPresenter.kt deleted file mode 100644 index 55369a82..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ChatsPresenter.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.meloda.fast.fragment.ui.presenter - -import android.os.Bundle -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.fast.adapter.ChatsAdapter -import com.meloda.fast.common.TimeManager -import com.meloda.fast.fragment.ui.repository.ChatsRepository -import com.meloda.fast.fragment.ui.view.ChatsView -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.listener.ItemLongClickListener -import com.meloda.fast.util.AndroidUtils -import com.meloda.mvp.MvpOnLoadListener -import com.meloda.mvp.MvpPresenter -import com.meloda.vksdk.model.VKConversation - -class ChatsPresenter(viewState: ChatsView) : - MvpPresenter( - viewState, ChatsRepository::class.java.name - ), - ItemClickListener, - ItemLongClickListener, - TimeManager.OnMinuteChangeListener { - - companion object { - const val DEFAULT_CONVERSATIONS_COUNT = 30 - } - - private lateinit var adapter: ChatsAdapter - - override fun onViewCreated(bundle: Bundle?) { - viewState.initViews() - } - - fun setup(recyclerView: RecyclerView, refreshLayout: SwipeRefreshLayout) { - viewState.prepareViews() - - createAdapter() - } - - private fun createAdapter() { - adapter = ChatsAdapter(requireContext(), arrayListOf()).also { - it.itemClickListener = this - it.itemLongClickListener = this - } - - } - - private fun fillAdapter(conversations: ArrayList, offset: Int) { - - } - - private fun getCachedConversations( - offset: Int = 0, - count: Int = DEFAULT_CONVERSATIONS_COUNT, - listener: MvpOnLoadListener? = null - ) { - listener?.onSuccess() - } - - private fun loadConversations( - offset: Int = 0, - count: Int = DEFAULT_CONVERSATIONS_COUNT, - listener: MvpOnLoadListener? = null - ) { - if (AndroidUtils.hasConnection()) { - - } else { - - } - } - - override fun onItemClick(position: Int) { - TODO("Not yet implemented") - } - - override fun onItemLongClick(position: Int) { - TODO("Not yet implemented") - } - - override fun onMinuteChange(currentMinute: Int) { - TODO("Not yet implemented") - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ConversationsPresenterDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ConversationsPresenterDeprecated.kt deleted file mode 100644 index c519fa36..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/ConversationsPresenterDeprecated.kt +++ /dev/null @@ -1,256 +0,0 @@ -package com.meloda.fast.fragment.ui.presenter - -import android.util.Log -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.arrayutils.ArrayUtils -import com.meloda.fast.BuildConfig -import com.meloda.fast.adapter.ConversationsAdapterDeprecated -import com.meloda.fast.adapter.diffutil.ConversationsCallbackDeprecated -import com.meloda.fast.common.TimeManager -import com.meloda.fast.fragment.ui.repository.ConversationsRepositoryDeprecated -import com.meloda.fast.fragment.ui.view.ConversationsViewDeprecated -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.listener.ItemLongClickListener -import com.meloda.fast.util.AndroidUtils -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpPresenter -import com.meloda.vksdk.model.VKConversation -import java.util.* - -class ConversationsPresenterDeprecated(viewState: ConversationsViewDeprecated) : - MvpPresenter( - viewState, - ConversationsRepositoryDeprecated::class.java.name - ), - ItemClickListener, - ItemLongClickListener, - TimeManager.OnMinuteChangeListener { - - companion object { - const val DEFAULT_CONVERSATIONS_COUNT = 30 - } - - private var conversationsCount: Int = 0 - - private lateinit var adapter: ConversationsAdapterDeprecated - - private lateinit var recyclerView: RecyclerView - private lateinit var layoutManager: LinearLayoutManager - - fun setup(recyclerView: RecyclerView, refreshLayout: SwipeRefreshLayout) { - this.recyclerView = recyclerView - this.context = recyclerView.context - this.layoutManager = recyclerView.layoutManager as LinearLayoutManager - - prepareViews() - -// setRecyclerViewScrollListener(recyclerView) - setRefreshLayoutListener(refreshLayout) - - createAdapter() - - TimeManager.addOnMinuteChangeListener(this) - - loadConversations(0, DEFAULT_CONVERSATIONS_COUNT) - -// getCachedConversations(0, DEFAULT_CONVERSATIONS_COUNT, object : MvpOnLoadListener { -// override fun onResponse(response: Any?) { -// setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) -// loadConversations(0, DEFAULT_CONVERSATIONS_COUNT) -// } -// -// override fun onError(t: Throwable) { -// setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) -// loadConversations(0, DEFAULT_CONVERSATIONS_COUNT) -// } -// }) - } - - private fun setRecyclerViewScrollListener(recyclerView: RecyclerView) { - recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - } - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - if (dy > 0) { - if (adapter.isLastItem() && !adapter.isLoading && adapter.itemCount < conversationsCount) { - adapter.isLoading = true - - val position = adapter.itemCount - 1 -// adapter.itemCount - 1 - (layoutManager.findLastCompletelyVisibleItemPosition() - layoutManager.findFirstCompletelyVisibleItemPosition()) - - setState(ListState.FILLED_LOADING) - if (AndroidUtils.hasConnection()) { - loadConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT, - object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - recyclerView.scrollToPosition(position) - - adapter.isLoading = false - } - - override fun onError(t: Throwable) { - viewState.showErrorSnackbar(t) - } - }) - } else { - getCachedConversations(adapter.itemCount, DEFAULT_CONVERSATIONS_COUNT, - object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - recyclerView.scrollToPosition(position) - - adapter.isLoading = false - } - - override fun onError(t: Throwable) { - viewState.showErrorSnackbar(t) - } - }) - } - - if (BuildConfig.DEBUG) - Log.d("RecyclerView", "Bottom reached") - } - } - } - }) - } - - private fun setRefreshLayoutListener(refreshLayout: SwipeRefreshLayout) { - refreshLayout.setOnRefreshListener { loadConversations() } - } - - private fun getCachedConversations( - offset: Int = 0, - count: Int = DEFAULT_CONVERSATIONS_COUNT, - listener: MvpOnResponseListener? = null - ) { - setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - - repository.getCachedConversations(offset, count, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - conversationsCount = response.size - - val conversations = ArrayUtils.cut(response, offset, count) - - fillAdapter(conversations, offset) - - setState(if (adapter.isEmpty()) ListState.EMPTY else ListState.FILLED) - - listener?.onResponse(null) - } - - override fun onError(t: Throwable) { - setState(if (adapter.isEmpty()) ListState.EMPTY_ERROR else ListState.FILLED) - - listener?.onError(t) - } - }) - } - - private fun loadConversations( - offset: Int = 0, - count: Int = DEFAULT_CONVERSATIONS_COUNT, - listener: MvpOnResponseListener? = null - ) { - if (!AndroidUtils.hasConnection()) { - setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED) - return - } else { - setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - } - - repository.loadConversations(offset, count, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - conversationsCount = VKConversation.conversationsCount - - fillAdapter(response, offset) - - setState(if (adapter.isEmpty()) ListState.EMPTY else ListState.FILLED) - - listener?.onResponse(null) - } - - override fun onError(t: Throwable) { - setState(if (adapter.isEmpty()) ListState.EMPTY_ERROR else ListState.FILLED) - - listener?.onError(t) - } - }) - } - - override fun destroy() { - adapter.destroy() - TimeManager.removeOnMinuteChangeListener(this) - } - - private fun createAdapter() { - adapter = ConversationsAdapterDeprecated(recyclerView, arrayListOf()).also { - it.itemClickListener = this - it.itemLongClickListener = this - } - - recyclerView.adapter = adapter - } - - private fun fillAdapter(conversations: ArrayList, offset: Int) { - val oldItems = ArrayList(adapter.values) - - if (offset > 0) { - adapter.addAll(conversations) - } else { - adapter.updateValues(conversations) - } - - adapter.notifyChanges(oldItems) - - if (offset == 0) recyclerView.scrollToPosition(0) - } - - override fun onItemClick(position: Int) { - openChat(adapter[position]) - } - - override fun onItemLongClick(position: Int) { - - } - - override fun onMinuteChange(currentMinute: Int) { - post { - adapter.notifyItemRangeChanged( - 0, - adapter.itemCount, - ConversationsCallbackDeprecated.DATE - ) - } - } - - private fun openChat(conversation: VKConversation) { -// TaskManager.execute { -// val peerUser = MemoryCache.getUserById(conversation.conversationId) -// val peerGroup = MemoryCache.getGroupById(conversation.conversationId) -// -// val extras = Bundle().also { -// it.putInt(MessagesActivityDeprecated.TAG_EXTRA_ID, conversation.conversationId) -// it.putString( -// MessagesActivityDeprecated.TAG_EXTRA_TITLE, -// VKUtil.getTitle(conversation, peerUser, peerGroup) -// ) -// it.putString( -// MessagesActivityDeprecated.TAG_EXTRA_AVATAR, -// VKUtil.getAvatar(conversation, peerUser, peerGroup) -// ) -// it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_USER, peerUser) -// it.putSerializable(MessagesActivityDeprecated.TAG_EXTRA_GROUP, peerGroup) -// } -// -// post { viewState.openChat(extras) } -// } - - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/FriendsPresenterDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/presenter/FriendsPresenterDeprecated.kt deleted file mode 100644 index c3e289d1..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/FriendsPresenterDeprecated.kt +++ /dev/null @@ -1,232 +0,0 @@ -package com.meloda.fast.fragment.ui.presenter - -import android.os.Bundle -import android.util.Log -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.meloda.arrayutils.ArrayUtils -import com.meloda.fast.activity.MessagesActivityDeprecated -import com.meloda.fast.adapter.UsersAdapterDeprecated -import com.meloda.fast.fragment.ui.repository.FriendsRepositoryDeprecated -import com.meloda.fast.fragment.ui.view.FriendsViewDeprecated -import com.meloda.fast.listener.ItemClickListener -import com.meloda.fast.util.AndroidUtils -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpPresenter -import com.meloda.vksdk.model.VKUser - -class FriendsPresenterDeprecated(viewState: FriendsViewDeprecated) : - MvpPresenter( - viewState, - FriendsRepositoryDeprecated::class.java.name - ), - ItemClickListener { - - companion object { - const val ONLY_ONLINE = "_only_online" - - const val DEFAULT_FRIENDS_COUNT = 30 - } - - private var userId: Int = 0 - private var friendsCount: Int = 0 - - private lateinit var adapter: UsersAdapterDeprecated - - private lateinit var recyclerView: RecyclerView - private lateinit var layoutManager: LinearLayoutManager - - fun setup(userId: Int, recyclerView: RecyclerView, refreshLayout: SwipeRefreshLayout) { - this.userId = userId - this.recyclerView = recyclerView - this.context = recyclerView.context - this.layoutManager = recyclerView.layoutManager as LinearLayoutManager - - setRecyclerViewScrollListener(recyclerView) - setRefreshListener(refreshLayout) - - createAdapter() - - getCachedFriends(userId, 0, DEFAULT_FRIENDS_COUNT, false, object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - setState(if (adapter.isEmpty()) MvpPresenter.ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - loadFriends(userId, 0, DEFAULT_FRIENDS_COUNT) - } - - override fun onError(t: Throwable) { - setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - loadFriends(userId, 0, DEFAULT_FRIENDS_COUNT) - } - }) - } - - private fun getCachedFriends( - userId: Int, - offset: Int = 0, - count: Int = DEFAULT_FRIENDS_COUNT, - onlyOnline: Boolean = false, - listener: MvpOnResponseListener? = null - ) { - setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - - repository.getCachedFriends( - userId, - offset, - count, - onlyOnline, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - val friends = ArrayUtils.cut(response, offset, count) - - fillAdapter(friends, offset) - - setState(if (adapter.isEmpty()) ListState.EMPTY else ListState.FILLED) - - listener?.onResponse(null) - } - - override fun onError(t: Throwable) { - setState(if (adapter.isEmpty()) ListState.EMPTY_ERROR else ListState.FILLED) - - listener?.onError(t) - } - }) - } - - private fun loadFriends( - userId: Int, - offset: Int = 0, - count: Int = DEFAULT_FRIENDS_COUNT, - onlyOnline: Boolean = false, - listener: MvpOnResponseListener? = null - ) { - if (!AndroidUtils.hasConnection()) { - setState(if (adapter.isEmpty()) ListState.EMPTY_NO_INTERNET else ListState.FILLED) - return - } else { - setState(if (adapter.isEmpty()) ListState.EMPTY_LOADING else ListState.FILLED_LOADING) - } - - repository.loadFriends( - userId, - offset, - count, - object : MvpOnResponseListener> { - override fun onResponse(response: ArrayList) { - friendsCount = VKUser.friendsCount - - fillAdapter(response, offset) - - setState(if (adapter.isEmpty()) ListState.EMPTY else ListState.FILLED) - - listener?.onResponse(null) - } - - override fun onError(t: Throwable) { - setState(if (adapter.isEmpty()) ListState.EMPTY_ERROR else ListState.FILLED) - - listener?.onError(t) - } - }) - } - - private fun setRecyclerViewScrollListener(recyclerView: RecyclerView) { - recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - } - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - if (dy > 0) { - if (adapter.isLastItem() && !adapter.isLoading && adapter.itemCount < friendsCount) { - adapter.isLoading = true - - val position = adapter.itemCount - 1 -// adapter.itemCount - 1 - (layoutManager.findLastCompletelyVisibleItemPosition() - layoutManager.findFirstCompletelyVisibleItemPosition()) - - setState(ListState.FILLED_LOADING) - if (AndroidUtils.hasConnection()) { - loadFriends( - userId, - adapter.itemCount, - DEFAULT_FRIENDS_COUNT, - false, - object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - recyclerView.scrollToPosition(position) - - adapter.isLoading = false - } - - override fun onError(t: Throwable) { - viewState.showErrorSnackbar(t) - } - }) - } else { - getCachedFriends( - userId, - adapter.itemCount, - DEFAULT_FRIENDS_COUNT, - false, - object : MvpOnResponseListener { - override fun onResponse(response: Any?) { - recyclerView.scrollToPosition(position) - - adapter.isLoading = false - } - - override fun onError(t: Throwable) { - viewState.showErrorSnackbar(t) - } - }) - } - - Log.d("RecyclerView", "Bottom reached") - } - } - } - }) - } - - private fun setRefreshListener(refreshLayout: SwipeRefreshLayout) { - refreshLayout.setOnRefreshListener { loadFriends(userId) } - } - - private fun createAdapter() { - adapter = UsersAdapterDeprecated(context!!, arrayListOf()).also { - it.itemClickListener = this - } - - recyclerView.adapter = adapter - } - - private fun fillAdapter(values: ArrayList, offset: Int) { - val oldItems = ArrayList(adapter.values) - - if (offset > 0) { - adapter.addAll(values) - } else { - adapter.updateValues(values) - } - -// adapter.notifyDataSetChanged() - adapter.notifyChanges(oldItems) - - if (offset == 0) recyclerView.scrollToPosition(0) - } - - private fun openChat(position: Int) { - val user = adapter[position] - - val data = Bundle().apply { - putInt(MessagesActivityDeprecated.TAG_EXTRA_ID, user.userId) - putString(MessagesActivityDeprecated.TAG_EXTRA_TITLE, user.toString()) - putString(MessagesActivityDeprecated.TAG_EXTRA_AVATAR, user.photo200) - } - } - - override fun onItemClick(position: Int) { - - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/LoginPresenter.kt b/app/src/main/java/com/meloda/fast/fragment/ui/presenter/LoginPresenter.kt deleted file mode 100644 index ec50e250..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/presenter/LoginPresenter.kt +++ /dev/null @@ -1,190 +0,0 @@ -package com.meloda.fast.fragment.ui.presenter - -import android.content.Context -import android.os.Bundle -import android.view.Gravity -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.LinearLayout -import androidx.appcompat.app.AlertDialog -import androidx.core.view.isVisible -import androidx.fragment.app.setFragmentResultListener -import com.google.android.material.textfield.TextInputEditText -import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.activity.MainActivity -import com.meloda.fast.common.AppGlobal -import com.meloda.fast.fragment.ChatsFragment -import com.meloda.fast.fragment.LoginFragment -import com.meloda.fast.fragment.ValidationFragment -import com.meloda.fast.fragment.ui.repository.LoginRepository -import com.meloda.fast.fragment.ui.view.LoginView -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpPresenter -import com.meloda.vksdk.VKApi -import com.squareup.picasso.Picasso -import org.json.JSONObject -import java.util.* - - -class LoginPresenter( - viewState: LoginView -) : MvpPresenter( - viewState, - LoginRepository::class.java.name -) { - - private var lastEmail: String = "" - private var lastPassword: String = "" - - private lateinit var fragment: LoginFragment - - fun onCreate(context: Context, fragment: LoginFragment, bundle: Bundle?) { - super.onCreate(context, bundle) - this.fragment = fragment - } - - override fun onViewCreated(bundle: Bundle?) { - viewState.initViews() - viewState.prepareViews() - } - - fun login( - email: String, - password: String, - captcha: String = "", - onResponseListener: MvpOnResponseListener? = null - ) { - lastEmail = email - lastPassword = password - - repository.login(requireContext(), email, password, captcha, - object : MvpOnResponseListener { - override fun onResponse(response: JSONObject) { - checkResponse(response, onResponseListener) - } - - override fun onError(t: Throwable) { - onResponseListener?.onError(t) - } - }) - } - - @Suppress("MoveVariableDeclarationIntoWhen") - private fun checkResponse( - response: JSONObject, - onResponseListener: MvpOnResponseListener? = null - ) { - if (response.has("error")) { - val errorString = response.optString("error") - when (errorString) { - "need_validation" -> { - val redirectUrl = response.optString("redirect_uri") - - val bundle = Bundle() - bundle.putString("url", redirectUrl) - - fragment.runOnUiThread { - fragment.setFragmentResultListener("validation") { _, bundle -> - val userId = bundle.getInt("userId") - val token = bundle.getString("token") ?: "" - saveUserData(userId, token) - - openMainScreen() - } - } - - fragment.parentFragmentManager.beginTransaction() - .replace( - R.id.fragmentContainer, - ValidationFragment().apply { arguments = bundle }) - .addToBackStack("") - .commit() - - } - "need_captcha" -> { - val captchaImage = response.optString("captcha_img") - val captchaSid = response.optString("captcha_sid") - showCaptchaDialog(captchaImage, captchaSid) - } - } - } else { - val userId = response.optInt("user_id", -1) - val token = response.optString("access_token") - saveUserData(userId, token) - - openMainScreen() - - onResponseListener?.onResponse(null) - } - } - - private fun openMainScreen() { - fragment.runOnUiThread { - VKApi.init(Locale.getDefault().language, UserConfig.token, AppGlobal.handler) - - (fragment.requireActivity() as MainActivity).bottomBar.isVisible = true - - fragment.parentFragmentManager.beginTransaction() - .replace( - R.id.fragmentContainer, - ChatsFragment() - ).commit() - } - } - - private fun saveUserData(userId: Int, token: String) { - UserConfig.userId = userId - UserConfig.token = token - UserConfig.save() - } - - private fun showCaptchaDialog(captchaImage: String, captchaSid: String) { - val resources = fragment.resources - val metrics = resources.displayMetrics - - fragment.runOnUiThread { - val image = ImageView(requireContext()) - image.layoutParams = ViewGroup.LayoutParams( - (metrics.widthPixels / 3.5).toInt(), metrics.heightPixels / 7 - ) - - Picasso.get().load(captchaImage).priority(Picasso.Priority.HIGH).into(image) - - val captchaCodeEditText = TextInputEditText(requireContext()) - captchaCodeEditText.setHint(R.string.captcha_hint) - - captchaCodeEditText.layoutParams = - LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - - val builder = AlertDialog.Builder(requireContext()) - - val layout = LinearLayout(requireContext()) - - layout.orientation = LinearLayout.VERTICAL - layout.gravity = Gravity.CENTER - layout.addView(image) - layout.addView(captchaCodeEditText) - - builder.setView(layout) - builder.setNegativeButton(android.R.string.cancel, null) - builder.setPositiveButton(android.R.string.ok) { _, _ -> - val captchaCode = captchaCodeEditText.text.toString().trim() - - login( - lastEmail, - lastPassword, - "&captcha_sid=$captchaSid&captcha_key=$captchaCode" - ) - } - - builder.setTitle(R.string.input_captcha) - builder.show() - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/repository/ChatsRepository.kt b/app/src/main/java/com/meloda/fast/fragment/ui/repository/ChatsRepository.kt deleted file mode 100644 index f9aa014d..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/repository/ChatsRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.meloda.fast.fragment.ui.repository - -import com.meloda.mvp.MvpRepository -import com.meloda.vksdk.model.VKConversation - -class ChatsRepository : MvpRepository() { -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/repository/ConversationsRepositoryDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/repository/ConversationsRepositoryDeprecated.kt deleted file mode 100644 index 10c5b0be..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/repository/ConversationsRepositoryDeprecated.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.meloda.fast.fragment.ui.repository - -import com.meloda.concurrent.TaskManager -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpRepository -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.VKConstants -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage - -class ConversationsRepositoryDeprecated : MvpRepository() { - - fun loadConversations( - offset: Int, count: Int, - listener: MvpOnResponseListener> - ) { - TaskManager.execute { - VKApi.messages() - .getConversations() - .filter("all") - .extended(true) - .fields(VKConstants.USER_FIELDS) - .offset(offset) - .count(count) - .executeArray(VKConversation::class.java, - object : OnResponseListener> { - override fun onResponse(response: ArrayList) { - TaskManager.execute { - cacheLoadedConversations(response) - -// MemoryCache.putUsers(VKConversation.profiles) -// MemoryCache.putGroups(VKConversation.groups) - - sendResponse(listener, response) - } - } - - override fun onError(t: Throwable) { - sendError(listener, t) - } - }) - } - } - - fun getCachedConversations( - offset: Int, count: Int, - listener: MvpOnResponseListener> - ) { - if (true) { - sendResponse(listener, arrayListOf()) - return - } - TaskManager.execute { -// val conversations = MemoryCache.getConversations().asArrayList() -// -// VKUtil.sortConversationsByDate(conversations, true) - -// sendResponse(listener, conversations) - } - } - - private fun fillConversationsWithProfilesAndGroups(conversations: ArrayList) { - for (conversation in conversations) { - val lastMessage = conversation.lastMessage - - when (conversation.type) { - VKConversation.Type.USER -> { -// VKUtil.searchUser(conversation.conversationId)?.let { -// conversation.peerUser = it -// } - } - - VKConversation.Type.GROUP -> { -// VKUtil.searchGroup(conversation.conversationId)?.let { -// conversation.peerGroup = it -// } - } - } - - if (lastMessage.isFromGroup()) { -// VKUtil.searchGroup(lastMessage.fromId)?.let { -// lastMessage.fromGroup = it -// } - } else { -// VKUtil.searchUser(lastMessage.fromId)?.let { -// lastMessage.fromUser = it -// } - } - } - } - - private fun cacheLoadedConversations(conversations: List) { - val messages = arrayListOf() - - for (conversation in conversations) { - messages.add(conversation.lastMessage) - } - -// MemoryCache.putMessages(messages) -// MemoryCache.putConversations(conversations) - } -} diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/repository/FriendsRepositoryDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/repository/FriendsRepositoryDeprecated.kt deleted file mode 100644 index a204db48..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/repository/FriendsRepositoryDeprecated.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.meloda.fast.fragment.ui.repository - -import android.util.Log -import com.meloda.concurrent.TaskManager -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpRepository -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.VKConstants -import com.meloda.vksdk.model.VKUser - -class FriendsRepositoryDeprecated : MvpRepository() { - - fun loadFriends( - userId: Int, - offset: Int, - count: Int, - listener: MvpOnResponseListener> - ) { - TaskManager.execute { - VKApi.friends() - .get() - .order("hints") - .userId(userId) - .fields(VKConstants.USER_FIELDS) - .count(count) - .offset(offset) - .executeArray(VKUser::class.java, - object : OnResponseListener> { - override fun onResponse(response: ArrayList) { - Log.d("FriendsRepository", "get ${response.size} friends from api") - - TaskManager.execute { - cacheLoadedUsers(userId, response) - } - - sendResponse(listener, response) - } - - override fun onError(t: Throwable) { - sendError(listener, t) - } - }) - } - } - - fun getCachedFriends( - userId: Int, offset: Int, count: Int, onlyOnline: Boolean, - listener: MvpOnResponseListener> - ) { -// TaskManager.execute { -// val friendsArray = MemoryCache.getFriends(userId) -// -// Log.d("FriendsRepository", "get ${friendsArray.size} friends from cache") -// -// if (friendsArray.isEmpty()) { -// sendError(listener, NullPointerException("Friends list is empty")) -// return@execute -// } -// -// val friends = arrayListOf() -// -// for (friend in friendsArray) { -// val user = MemoryCache.getUserById(friend.friendId) -// -// user?.let { -// if (onlyOnline && user.isOnline || !onlyOnline) { -// friends.add(user) -// } -// } -// } -// -// sendResponse(listener, friends) -// } - } - - private fun cacheLoadedUsers(userId: Int, users: ArrayList) { -// MemoryCache.putUsers(users) -// -// val friends = ArrayList() -// -// for (user in users) { -// friends.add(VKFriend(user.userId, userId)) -// } - -// MemoryCache.putFriends(friends) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/repository/LoginRepository.kt b/app/src/main/java/com/meloda/fast/fragment/ui/repository/LoginRepository.kt deleted file mode 100644 index ff48425e..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/repository/LoginRepository.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.meloda.fast.fragment.ui.repository - -import android.annotation.SuppressLint -import android.content.Context -import android.webkit.CookieManager -import android.webkit.JavascriptInterface -import android.webkit.WebView -import android.webkit.WebViewClient -import com.meloda.mvp.MvpOnResponseListener -import com.meloda.mvp.MvpRepository -import com.meloda.vksdk.VKAuth -import org.json.JSONObject -import org.jsoup.Jsoup - -class LoginRepository : MvpRepository() { - - fun login( - context: Context, - email: String, - password: String, - captcha: String, - onResponseListener: MvpOnResponseListener - ) { - if (email.trim().isEmpty() || password.trim().isEmpty()) return - val loadingUrl = VKAuth.getDirectAuthUrl(email, password, captcha) - - val webView = createWebView(context) - - webView.addJavascriptInterface(WebViewHandlerInterface(onResponseListener), "HtmlHandler") - webView.webViewClient = object : WebViewClient() { - override fun onPageFinished(view: WebView?, url: String?) { - webView.loadUrl( - "javascript:window.HtmlHandler.handleHtml" + - "(''+document.getElementsByTagName('html')[0].innerHTML+'');" - ) - } - } - - webView.loadUrl(loadingUrl) - } - - @SuppressLint("SetJavaScriptEnabled") - private fun createWebView(context: Context): WebView { - val loginWebView = WebView(context) - - loginWebView.settings.javaScriptEnabled = true - loginWebView.settings.domStorageEnabled = true - loginWebView.settings.loadsImagesAutomatically = false - loginWebView.settings.userAgentString = "Chrome/41.0.2228.0 Safari/537.36" - - loginWebView.clearCache(true) - - val cookieManager = CookieManager.getInstance() - cookieManager.removeAllCookies(null) - cookieManager.flush() - cookieManager.setAcceptCookie(false) - - return loginWebView - } - - private class WebViewHandlerInterface(private var onResponseListener: MvpOnResponseListener) { - @JavascriptInterface - fun handleHtml(html: String?) { - val doc = Jsoup.parse(html) - - val responseString = - doc.select("pre[style=\"word-wrap: break-word; white-space: pre-wrap;\"]") - .first() - .text() - - onResponseListener.onResponse(JSONObject(responseString)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/view/ChatsView.kt b/app/src/main/java/com/meloda/fast/fragment/ui/view/ChatsView.kt deleted file mode 100644 index 65f185f9..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/view/ChatsView.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.meloda.fast.fragment.ui.view - -import com.meloda.mvp.MvpView - -interface ChatsView : MvpView { - - fun initViews() - - fun prepareViews() - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/view/ConversationsViewDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/view/ConversationsViewDeprecated.kt deleted file mode 100644 index bb53bed2..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/view/ConversationsViewDeprecated.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.meloda.fast.fragment.ui.view - -import android.os.Bundle -import com.meloda.mvp.MvpView - -interface ConversationsViewDeprecated : MvpView { - - fun openChat(extras: Bundle) - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/view/FriendsViewDeprecated.kt b/app/src/main/java/com/meloda/fast/fragment/ui/view/FriendsViewDeprecated.kt deleted file mode 100644 index 2bf0ba3b..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/view/FriendsViewDeprecated.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.meloda.fast.fragment.ui.view - -import android.os.Bundle -import com.meloda.mvp.MvpView - -interface FriendsViewDeprecated : MvpView { - - fun openChat(extras: Bundle) - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/ui/view/LoginView.kt b/app/src/main/java/com/meloda/fast/fragment/ui/view/LoginView.kt deleted file mode 100644 index dc55c615..00000000 --- a/app/src/main/java/com/meloda/fast/fragment/ui/view/LoginView.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.meloda.fast.fragment.ui.view - -import com.meloda.mvp.MvpView - -interface LoginView : MvpView { - - fun initViews() - - fun prepareViews() - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/item/SimpleMenuItem.kt b/app/src/main/java/com/meloda/fast/item/SimpleMenuItem.kt deleted file mode 100644 index c580e72f..00000000 --- a/app/src/main/java/com/meloda/fast/item/SimpleMenuItem.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.meloda.fast.item - -import android.graphics.drawable.Drawable -import android.view.View - -data class SimpleMenuItem( - val icon: Drawable?, - val title: String, - var clickListener: View.OnClickListener? = null -) diff --git a/app/src/main/java/com/meloda/fast/listener/ItemClickListener.kt b/app/src/main/java/com/meloda/fast/listener/ItemClickListener.kt deleted file mode 100644 index 7780ad62..00000000 --- a/app/src/main/java/com/meloda/fast/listener/ItemClickListener.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.meloda.fast.listener - -interface ItemClickListener { - fun onItemClick(position: Int) -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/listener/ItemLongClickListener.kt b/app/src/main/java/com/meloda/fast/listener/ItemLongClickListener.kt deleted file mode 100644 index 4f7b095f..00000000 --- a/app/src/main/java/com/meloda/fast/listener/ItemLongClickListener.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.meloda.fast.listener - -interface ItemLongClickListener { - fun onItemLongClick(position: Int) -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/util/ImageUtils.kt b/app/src/main/java/com/meloda/fast/util/ImageUtils.kt deleted file mode 100644 index fe12c2ab..00000000 --- a/app/src/main/java/com/meloda/fast/util/ImageUtils.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.meloda.fast.util - -import android.graphics.Bitmap -import android.graphics.drawable.Drawable -import android.widget.ImageView -import com.facebook.drawee.view.SimpleDraweeView -import com.squareup.picasso.Picasso -import com.squareup.picasso.Target - -object ImageUtils { - - fun loadImage(image: String, imageView: ImageView, placeholder: Drawable?) { - if (image.isEmpty()) return - - if (imageView is SimpleDraweeView) { - imageView.setImageURI(image) - return - } - - val picasso = Picasso.get() - .load(image) - .priority(Picasso.Priority.LOW) - - if (placeholder != null) picasso.placeholder(placeholder) - - picasso.into(imageView) - } - - fun loadImage(image: String?, listener: OnLoadListener?) { - if (image.isNullOrEmpty()) return - - val picasso = Picasso.get() - .load(image) - .priority(Picasso.Priority.LOW) - - val target = object : Target { - override fun onPrepareLoad(placeHolderDrawable: Drawable?) { - - } - - override fun onBitmapFailed(e: Exception, errorDrawable: Drawable?) { - listener?.onError(e) - } - - override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { - listener?.onLoad(bitmap) - } - } - - picasso.into(target) - } - - - interface OnLoadListener { - fun onLoad(bitmap: Bitmap) - fun onError(e: Exception) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/util/KeyboardUtils.kt b/app/src/main/java/com/meloda/fast/util/KeyboardUtils.kt deleted file mode 100644 index 0edeb181..00000000 --- a/app/src/main/java/com/meloda/fast/util/KeyboardUtils.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.meloda.fast.util - -import android.view.View -import com.meloda.fast.common.AppGlobal - -object KeyboardUtils { - - fun hideKeyboardFrom(view: View) { - AppGlobal.inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0) - } - - fun showKeyboard(focusedView: View) { - AppGlobal.inputMethodManager.showSoftInput(focusedView, 0) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/widget/RecyclerView.kt b/app/src/main/java/com/meloda/fast/widget/RecyclerView.kt deleted file mode 100644 index 4252f1c1..00000000 --- a/app/src/main/java/com/meloda/fast/widget/RecyclerView.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.meloda.fast.widget - -import android.content.Context -import android.util.AttributeSet -import androidx.recyclerview.widget.RecyclerView - -class RecyclerView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : RecyclerView(context, attrs, defStyleAttr) { - - var noItemsView: NoItemsView? = null - - -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/UserConfig.kt b/app/src/main/kotlin/com/meloda/fast/UserConfig.kt new file mode 100644 index 00000000..bfdab4a1 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/UserConfig.kt @@ -0,0 +1,35 @@ +package com.meloda.fast + +import android.text.TextUtils +import com.meloda.fast.common.AppGlobal + +object UserConfig { + + private const val TOKEN = "token" + private const val USER_ID = "user_id" + + const val API_ID = "6964679" + + var userId: Int = -1 + get() = AppGlobal.preferences.getInt(USER_ID, -1) + set(value) { + field = value + AppGlobal.preferences.edit().putInt(USER_ID, value).apply() + } + + var accessToken: String = "" + get() = AppGlobal.preferences.getString(TOKEN, "") ?: "" + set(value) { + field = value + AppGlobal.preferences.edit().putString(TOKEN, value).apply() + } + + fun clear() { + accessToken = "" + userId = -1 + } + + fun isLoggedIn(): Boolean { + return userId > 0 && !TextUtils.isEmpty(accessToken) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/VKLongPollParser.kt b/app/src/main/kotlin/com/meloda/fast/VKLongPollParser.kt similarity index 95% rename from app/src/main/java/com/meloda/fast/VKLongPollParser.kt rename to app/src/main/kotlin/com/meloda/fast/VKLongPollParser.kt index c6017076..738efeac 100644 --- a/app/src/main/java/com/meloda/fast/VKLongPollParser.kt +++ b/app/src/main/kotlin/com/meloda/fast/VKLongPollParser.kt @@ -2,11 +2,11 @@ package com.meloda.fast import android.util.Log import androidx.annotation.WorkerThread -import com.meloda.concurrent.EventInfo -import com.meloda.concurrent.TaskManager -import com.meloda.vksdk.VKApiKeys -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.concurrent.EventInfo +import com.meloda.fast.concurrent.TaskManager +import com.meloda.fast.api.VKApiKeys +import com.meloda.fast.api.model.VKMessage +import com.meloda.fast.api.util.VKUtil import org.json.JSONArray @Suppress("UNCHECKED_CAST") diff --git a/app/src/main/kotlin/com/meloda/fast/activity/MainActivity.kt b/app/src/main/kotlin/com/meloda/fast/activity/MainActivity.kt new file mode 100644 index 00000000..360db60b --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/activity/MainActivity.kt @@ -0,0 +1,19 @@ +package com.meloda.fast.activity + +import android.os.Bundle +import android.viewbinding.library.activity.viewBinding +import com.meloda.fast.R +import com.meloda.fast.base.BaseActivity +import com.meloda.fast.databinding.ActivityMainBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainActivity : BaseActivity(R.layout.activity_main) { + + private val binding: ActivityMainBinding by viewBinding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/Answer.kt b/app/src/main/kotlin/com/meloda/fast/api/Answer.kt new file mode 100644 index 00000000..f6fbffb5 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/Answer.kt @@ -0,0 +1,6 @@ +package com.meloda.fast.api + +sealed class Answer { + data class Success(val data: T) : Answer() + data class Error(val errorString: String) : Answer() +} \ No newline at end of file diff --git a/vksdk/src/main/java/com/meloda/vksdk/ErrorCodes.kt b/app/src/main/kotlin/com/meloda/fast/api/ErrorCodes.kt similarity index 98% rename from vksdk/src/main/java/com/meloda/vksdk/ErrorCodes.kt rename to app/src/main/kotlin/com/meloda/fast/api/ErrorCodes.kt index f537f83e..748594f0 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/ErrorCodes.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/ErrorCodes.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk +package com.meloda.fast.api object ErrorCodes { const val UNKNOWN_ERROR = 1 diff --git a/vksdk/src/main/java/com/meloda/vksdk/OnResponseListener.kt b/app/src/main/kotlin/com/meloda/fast/api/OnResponseListener.kt similarity index 78% rename from vksdk/src/main/java/com/meloda/vksdk/OnResponseListener.kt rename to app/src/main/kotlin/com/meloda/fast/api/OnResponseListener.kt index 0ab0ba6e..57e036bb 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/OnResponseListener.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/OnResponseListener.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk +package com.meloda.fast.api interface OnResponseListener { diff --git a/app/src/main/kotlin/com/meloda/fast/api/Requests.kt b/app/src/main/kotlin/com/meloda/fast/api/Requests.kt new file mode 100644 index 00000000..2e7b6b8a --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/Requests.kt @@ -0,0 +1,2 @@ +package com.meloda.fast.api + diff --git a/app/src/main/kotlin/com/meloda/fast/api/Resource.kt b/app/src/main/kotlin/com/meloda/fast/api/Resource.kt new file mode 100644 index 00000000..7d00e0e2 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/Resource.kt @@ -0,0 +1,25 @@ +package com.meloda.fast.api + +data class Resource constructor( + val status: Status, + val responseData: T?, + val message: String? +) { + + enum class Status { + SUCCESS, + ERROR, + LOADING + } + + companion object { + fun success(responseData: T?): Resource = + Resource(Status.SUCCESS, responseData, null) + + fun error(message: String?, responseBody: T? = null): Resource = + Resource(Status.ERROR, responseBody, message) + + fun loading(responseData: T? = null): Resource = + Resource(Status.LOADING, responseData, null) + } +} diff --git a/vksdk/src/main/java/com/meloda/vksdk/VKApi.kt b/app/src/main/kotlin/com/meloda/fast/api/VKApi.kt similarity index 74% rename from vksdk/src/main/java/com/meloda/vksdk/VKApi.kt rename to app/src/main/kotlin/com/meloda/fast/api/VKApi.kt index 77cfd7b6..a91b9112 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/VKApi.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/VKApi.kt @@ -1,18 +1,21 @@ -package com.meloda.vksdk +package com.meloda.fast.api import android.os.Handler import android.util.Log import androidx.annotation.WorkerThread -import com.meloda.concurrent.TaskManager -import com.meloda.netservices.HttpRequest -import com.meloda.vksdk.method.MessageMethodSetter -import com.meloda.vksdk.method.MethodSetter -import com.meloda.vksdk.method.UserMethodSetter -import com.meloda.vksdk.model.* +import com.meloda.fast.BuildConfig +import com.meloda.fast.api.method.MessageMethodSetter +import com.meloda.fast.api.method.MethodSetter +import com.meloda.fast.api.method.UserMethodSetter +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow import org.json.JSONArray import org.json.JSONObject import java.util.* import kotlin.collections.ArrayList +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine @Suppress("UNCHECKED_CAST") object VKApi { @@ -41,7 +44,8 @@ object VKApi { Log.w(TAG, "url: $url") } - val buffer = HttpRequest[url].asString() + val buffer = com.meloda.fast.net.HttpRequest[url].asString() + if (BuildConfig.DEBUG) { Log.i(TAG, "response: $buffer") } @@ -63,9 +67,9 @@ object VKApi { when (cls) { null -> return null - VKLongPollServer::class.java -> { + com.meloda.fast.api.model.VKLongPollServer::class.java -> { json.optJSONObject("response")?.let { - return arrayListOf(VKLongPollServer(it)) as ArrayList? + return arrayListOf(com.meloda.fast.api.model.VKLongPollServer(it)) as ArrayList? } } @@ -91,50 +95,56 @@ object VKApi { val models = ArrayList(array.length()) when (cls) { - VKUser::class.java -> { + com.meloda.fast.api.model.VKUser::class.java -> { json.optJSONObject("response")?.let { r -> - VKUser.friendsCount = r.optInt("count") + com.meloda.fast.api.model.VKUser.friendsCount = r.optInt("count") } for (i in 0 until array.length()) { - models.add(VKUser(array.optJSONObject(i)) as T) + models.add(com.meloda.fast.api.model.VKUser(array.optJSONObject(i)) as T) } } - VKMessage::class.java -> { + com.meloda.fast.api.model.VKMessage::class.java -> { response as JSONObject if (url.contains("messages.getHistory")) { - VKMessage.lastHistoryCount = response.optInt("count") + com.meloda.fast.api.model.VKMessage.lastHistoryCount = response.optInt("count") response.optJSONArray("profiles")?.let { - val profiles = arrayListOf() + val profiles = arrayListOf() for (j in 0 until it.length()) { - profiles.add(VKUser(it.optJSONObject(j))) + profiles.add(com.meloda.fast.api.model.VKUser(it.optJSONObject(j))) } - VKMessage.profiles = profiles + com.meloda.fast.api.model.VKMessage.profiles = profiles } response.optJSONArray("groups")?.let { - val groups = arrayListOf() + val groups = arrayListOf() for (j in 0 until it.length()) { - groups.add(VKGroup(it.optJSONObject(j))) + groups.add(com.meloda.fast.api.model.VKGroup(it.optJSONObject(j))) } - VKMessage.groups = groups + com.meloda.fast.api.model.VKMessage.groups = groups } response.optJSONArray("conversations")?.let { - val conversations = arrayListOf() + val conversations = arrayListOf() for (j in 0 until it.length()) { - conversations.add(VKConversation(it.optJSONObject(j))) + conversations.add( + com.meloda.fast.api.model.VKConversation( + it.optJSONObject( + j + ) + ) + ) } - VKMessage.conversations = conversations + com.meloda.fast.api.model.VKMessage.conversations = conversations } } @@ -144,35 +154,35 @@ object VKApi { source = source.optJSONObject("message") } - val message = VKMessage(source) + val message = com.meloda.fast.api.model.VKMessage(source) models.add(message as T) } } - VKGroup::class.java -> { + com.meloda.fast.api.model.VKGroup::class.java -> { for (i in 0 until array.length()) { - models.add(VKGroup(array.optJSONObject(i)) as T) + models.add(com.meloda.fast.api.model.VKGroup(array.optJSONObject(i)) as T) } } - VKModel::class.java -> { + com.meloda.fast.api.model.VKModel::class.java -> { if (url.contains("messages.getHistoryAttachments")) { - return VKAttachments.parse(array) as ArrayList + return com.meloda.fast.api.model.VKAttachments.parse(array) as ArrayList } } - VKConversation::class.java -> { + com.meloda.fast.api.model.VKConversation::class.java -> { if (url.contains("getConversationsById")) { for (i in 0 until array.length()) { val source = array.optJSONObject(i) - models.add(VKConversation(source) as T) + models.add(com.meloda.fast.api.model.VKConversation(source) as T) } return models } json.optJSONObject("response")?.let { r -> - VKConversation.conversationsCount = r.optInt("count") + com.meloda.fast.api.model.VKConversation.conversationsCount = r.optInt("count") } for (i in 0 until array.length()) { @@ -182,28 +192,28 @@ object VKApi { val oConversation = source.optJSONObject("conversation") ?: return null val oLastMessage = source.optJSONObject("last_message") ?: return null - val conversation = VKConversation(oConversation).also { - it.lastMessage = VKMessage(oLastMessage) + val conversation = com.meloda.fast.api.model.VKConversation(oConversation).also { + it.lastMessage = com.meloda.fast.api.model.VKMessage(oLastMessage) } response.optJSONArray("profiles")?.let { - val profiles = arrayListOf() + val profiles = arrayListOf() for (j in 0 until it.length()) { - profiles.add(VKUser(it.optJSONObject(j))) + profiles.add(com.meloda.fast.api.model.VKUser(it.optJSONObject(j))) } - VKConversation.profiles = profiles + com.meloda.fast.api.model.VKConversation.profiles = profiles } response.optJSONArray("groups")?.let { - val groups = arrayListOf() + val groups = arrayListOf() for (j in 0 until it.length()) { - groups.add(VKGroup(it.optJSONObject(j))) + groups.add(com.meloda.fast.api.model.VKGroup(it.optJSONObject(j))) } - VKConversation.groups = groups + com.meloda.fast.api.model.VKConversation.groups = groups } models.add(conversation as T) @@ -215,21 +225,35 @@ object VKApi { } fun execute(url: String, cls: Class, listener: OnResponseListener?) { - TaskManager.execute { + com.meloda.fast.concurrent.TaskManager.execute { try { - val models = execute(url, cls) + val models = execute(url, cls) ?: return@execute - listener?.let { SuccessCallback(listener, models as E) } +// listener?.onResponse(models) } catch (e: Exception) { e.printStackTrace() + listener?.onError(e) +// it.resumeWithException(e) + } + } - listener?.let { ErrorCallback(listener, e) } + } + + suspend fun suspendExecute(url: String, cls: Class): Flow { + return suspendCoroutine { + try { + val models = execute(url, cls)?.asFlow() ?: return@suspendCoroutine + + it.resume(models) + } catch (e: Exception) { + e.printStackTrace() + it.resumeWithException(e) } } } fun executeArray(url: String, cls: Class, listener: OnResponseListener>) { - TaskManager.execute { + com.meloda.fast.concurrent.TaskManager.execute { try { val models = execute(url, cls) diff --git a/vksdk/src/main/java/com/meloda/vksdk/VKApiKeys.kt b/app/src/main/kotlin/com/meloda/fast/api/VKApiKeys.kt similarity index 93% rename from vksdk/src/main/java/com/meloda/vksdk/VKApiKeys.kt rename to app/src/main/kotlin/com/meloda/fast/api/VKApiKeys.kt index ea4ef817..91ac7211 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/VKApiKeys.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/VKApiKeys.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk +package com.meloda.fast.api enum class VKApiKeys(val value: String) { READ_MESSAGE("_read_message"), diff --git a/vksdk/src/main/java/com/meloda/vksdk/VKAuth.kt b/app/src/main/kotlin/com/meloda/fast/api/VKAuth.kt similarity index 73% rename from vksdk/src/main/java/com/meloda/vksdk/VKAuth.kt rename to app/src/main/kotlin/com/meloda/fast/api/VKAuth.kt index dbdcef4d..89ddb98a 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/VKAuth.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/VKAuth.kt @@ -1,7 +1,8 @@ -package com.meloda.vksdk +package com.meloda.fast.api import android.util.Log -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.BuildConfig +import com.meloda.fast.api.util.VKUtil import java.net.URLEncoder object VKAuth { @@ -26,14 +27,19 @@ object VKAuth { const val redirectUrl = "https://oauth.vk.com/blank.html" - fun getDirectAuthUrl(login: String, password: String, captcha: String = ""): String { + fun getDirectAuthUrl( + login: String, + password: String, + captchaSid: String? = null, + captchaKey: String? = null + ): String { return "https://oauth.vk.com/token?grant_type=password&" + - "client_id=6146827&" + + "client_id=${VKConstants.VK_APP_ID}&" + "scope=$settings&" + - "client_secret=qVxWRF1CwHERuIrKBnqe&" + + "client_secret=${VKConstants.VK_APP_SECRET}&" + "username=$login&" + "password=$password" + - (if (captcha.isEmpty()) "" else "&$captcha") + + (if (captchaSid == null || captchaKey == null) "" else "&captcha_sid=$captchaSid&captcha_key=$captchaKey") + "&v=${VKApi.API_VERSION}" // return "https://oauth.vk.com/token?grant_type=password&" + // "client_id=2274003&" + @@ -60,20 +66,19 @@ object VKAuth { "v=${URLEncoder.encode(VKApi.API_VERSION, "utf-8")}" } - @Throws(Exception::class) - fun parseRedirectUrl(url: String): Array { - val accessToken = VKUtil.extractPattern(url, "access_token=(.*?)&") - val userId = VKUtil.extractPattern(url, "user_id=(\\d*)") + fun parseRedirectUrl(url: String): Pair { + val accessToken = VKUtil.extractPattern(url, "access_token=(.*?)&") ?: "" + val userId = VKUtil.extractPattern(url, "user_id=(\\d*)")?.toIntOrNull() ?: -1 if (BuildConfig.DEBUG) { Log.i(TAG, "access_token=$accessToken") Log.i(TAG, "user_id=$userId") } - if (userId == null || userId.isEmpty() || accessToken == null || accessToken.isEmpty()) throw Exception( - "Failed to parse redirect url $url" + if (accessToken.isEmpty() || userId == -1) throw Exception( + "Failed to parse redirect url: $url" ) - return arrayOf(accessToken, userId) + return accessToken to userId } } \ No newline at end of file diff --git a/vksdk/src/main/java/com/meloda/vksdk/VKConstants.kt b/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt similarity index 83% rename from vksdk/src/main/java/com/meloda/vksdk/VKConstants.kt rename to app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt index 841072c2..dd3bf81e 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/VKConstants.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/VKConstants.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk +package com.meloda.fast.api object VKConstants { @@ -8,6 +8,12 @@ object VKConstants { const val USER_FIELDS = "photo_50,photo_100,photo_200,status,screen_name,online,online_mobile,last_seen,verified,sex" + const val VK_APP_ID = "2274003" + const val VK_APP_SECRET = "hHbZxrka2uZ6jB1inYsH" + + const val VK_ME_ID = "6146827" + const val VK_ME_SECRET = "qVxWRF1CwHERuIrKBnqe" + /* const val ACTION_CHAT_CREATE = "chat_create" diff --git a/vksdk/src/main/java/com/meloda/vksdk/VKException.kt b/app/src/main/kotlin/com/meloda/fast/api/VKException.kt similarity index 68% rename from vksdk/src/main/java/com/meloda/vksdk/VKException.kt rename to app/src/main/kotlin/com/meloda/fast/api/VKException.kt index ed567195..9c2a94a2 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/VKException.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/VKException.kt @@ -1,8 +1,8 @@ -package com.meloda.vksdk +package com.meloda.fast.api import java.io.IOException -class VKException(var url: String, override var message: String, var code: Int) : +class VKException(var url: String = "", override var message: String = "", var code: Int) : IOException(message) { var captchaSid: String? = null var captchaImg: String? = null diff --git a/app/src/main/kotlin/com/meloda/fast/api/VKRepo.kt b/app/src/main/kotlin/com/meloda/fast/api/VKRepo.kt new file mode 100644 index 00000000..97ebfc17 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/VKRepo.kt @@ -0,0 +1,17 @@ +package com.meloda.fast.api + +import com.meloda.fast.api.model.response.GetConversationsResponse +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.POST + +interface VKRepo { + + @FormUrlEncoded + @POST(VKUrls.getConversations) + suspend fun getAllChats( + @Field("user_id") chatId: Int, + @Field("token") token: String + ): Answer + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/VKUrls.kt b/app/src/main/kotlin/com/meloda/fast/api/VKUrls.kt new file mode 100644 index 00000000..b00610f3 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/VKUrls.kt @@ -0,0 +1,7 @@ +package com.meloda.fast.api + +object VKUrls { + + const val getConversations = "messages.getConversations" + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt new file mode 100644 index 00000000..b67c4553 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/MessagesDataSource.kt @@ -0,0 +1,4 @@ +package com.meloda.fast.api.datasource + +class MessagesDataSource constructor() { +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/datasource/base/BaseDataSource.kt b/app/src/main/kotlin/com/meloda/fast/api/datasource/base/BaseDataSource.kt new file mode 100644 index 00000000..5147bda6 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/datasource/base/BaseDataSource.kt @@ -0,0 +1,97 @@ +package com.meloda.fast.api.datasource.base + +import android.util.Log +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.google.gson.JsonSyntaxException +import com.meloda.fast.api.Resource +import com.meloda.fast.api.model.ApiResponse +import com.meloda.fast.api.ErrorCodes +import com.meloda.fast.api.VKException +import okhttp3.ResponseBody +import retrofit2.HttpException + +class BaseDataSource { + + private val TAG = BaseDataSource::class.simpleName + + //TODO: move to resources + private val DEFAULT_ERROR = "Internal server error" + + protected suspend fun getResult(apiCall: suspend () -> ApiResponse): Resource { + try { + val response = apiCall() + return if (response.isSuccessful) { + Resource.success(response.response) + } else { + Log.d(TAG, "Server response unsuccessful") + if (response.error != null) { + Log.w(TAG, "Unsuccessful response with code 2XX") + Resource.error(response.error.message, response.response) + } else { + Log.e(TAG, "Unsuccessful result without error!") + Resource.error(DEFAULT_ERROR) + } + } + } catch (e: HttpException) { + Log.e(TAG, "Error while executing request ${e.message}") + val errorBody = e.response()?.errorBody() ?: return Resource.error(DEFAULT_ERROR) + val errorResponse = parseErrorBody(errorBody) ?: return Resource.error(DEFAULT_ERROR) + + return Resource.error(errorResponse.message) + } catch (e: Exception) { + Log.e(TAG, "Error while executing request ${e.message}") + + return Resource.error(DEFAULT_ERROR) + } + } + + private fun parseErrorBody(responseBody: ResponseBody?): Exception? { + if (responseBody == null) return null + + val jsonResponse: JsonObject? + try { + jsonResponse = JsonParser.parseString(responseBody.string()) as? JsonObject + if (jsonResponse == null) { + Log.d(TAG, "Response body is empty while parsing error body.") + return null + } + } catch (e: JsonSyntaxException) { + Log.e(TAG, "Error while parsing json ${e.message}") + return null + } catch (e: java.lang.Exception) { + Log.e(TAG, "Unknown error ${e.message}") + return null + } + + if (jsonResponse.has("error")) { + val error = jsonResponse["error"].asJsonObject + + val message = error["error_msg"].asString + val code = error["error_code"].asInt + + val e = VKException("", message, code) + + //TODO: add checking invalid session + if (code == 5 && message.contains("invalid session")) { +// context?.startActivity(Intent(context, DropUserDataActivity::class.java).apply { +// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) +// }) + } + + if (code == ErrorCodes.CAPTCHA_NEEDED) { + e.captchaImg = error["captcha_img"].asString + e.captchaSid = error["captcha_sid"].asString + } + + if (code == ErrorCodes.VALIDATION_REQUIRED) { + e.redirectUri = error["redirect_uri"].asString + } + + return e + } + + return null + } + +} \ No newline at end of file diff --git a/vksdk/src/main/java/com/meloda/vksdk/method/MessageMethodSetter.kt b/app/src/main/kotlin/com/meloda/fast/api/method/MessageMethodSetter.kt similarity index 88% rename from vksdk/src/main/java/com/meloda/vksdk/method/MessageMethodSetter.kt rename to app/src/main/kotlin/com/meloda/fast/api/method/MessageMethodSetter.kt index 66559b07..d8c5e2d5 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/method/MessageMethodSetter.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/method/MessageMethodSetter.kt @@ -1,6 +1,6 @@ -package com.meloda.vksdk.method +package com.meloda.fast.api.method -import com.meloda.arrayutils.ArrayUtils +import com.meloda.fast.util.ArrayUtils class MessageMethodSetter(name: String) : MethodSetter(name) { @@ -60,7 +60,7 @@ class MessageMethodSetter(name: String) : MethodSetter(name) { } fun peerIds(vararg values: Int): MessageMethodSetter { - put("peer_ids", ArrayUtils.asString(values)) + put("peer_ids", com.meloda.fast.util.ArrayUtils.asString(values)) return this } @@ -100,22 +100,22 @@ class MessageMethodSetter(name: String) : MethodSetter(name) { } fun attachment(attachments: Collection): MessageMethodSetter { - put("attachment", ArrayUtils.asString(attachments)) + put("attachment", com.meloda.fast.util.ArrayUtils.asString(attachments)) return this } fun attachment(vararg attachments: String): MessageMethodSetter { - put("attachment", ArrayUtils.asString(*attachments)) + put("attachment", com.meloda.fast.util.ArrayUtils.asString(*attachments)) return this } fun forwardMessages(ids: Collection): MessageMethodSetter { - put("forward_messages", ArrayUtils.asString(ids)) + put("forward_messages", com.meloda.fast.util.ArrayUtils.asString(ids)) return this } fun forwardMessages(vararg ids: Int): MessageMethodSetter { - put("forward_messages", ArrayUtils.asString(ids)) + put("forward_messages", com.meloda.fast.util.ArrayUtils.asString(ids)) return this } @@ -160,12 +160,12 @@ class MessageMethodSetter(name: String) : MethodSetter(name) { } fun chatIds(vararg ids: Int): MessageMethodSetter { - put("max_msg_id", ArrayUtils.asString(ids)) + put("max_msg_id", com.meloda.fast.util.ArrayUtils.asString(ids)) return this } fun chatIds(ids: Collection): MessageMethodSetter { - put("max_msg_id", ArrayUtils.asString(ids)) + put("max_msg_id", com.meloda.fast.util.ArrayUtils.asString(ids)) return this } diff --git a/vksdk/src/main/java/com/meloda/vksdk/method/MethodSetter.kt b/app/src/main/kotlin/com/meloda/fast/api/method/MethodSetter.kt similarity index 86% rename from vksdk/src/main/java/com/meloda/vksdk/method/MethodSetter.kt rename to app/src/main/kotlin/com/meloda/fast/api/method/MethodSetter.kt index 084885fd..dbebf7eb 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/method/MethodSetter.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/method/MethodSetter.kt @@ -1,11 +1,11 @@ -package com.meloda.vksdk.method +package com.meloda.fast.api.method import android.util.ArrayMap import android.util.Log -import com.meloda.arrayutils.ArrayUtils -import com.meloda.vksdk.BuildConfig -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.VKApi +import com.meloda.fast.BuildConfig +import com.meloda.fast.api.OnResponseListener +import com.meloda.fast.api.VKApi +import kotlinx.coroutines.flow.Flow import java.net.URLEncoder @Suppress("UNCHECKED_CAST") @@ -79,6 +79,10 @@ open class MethodSetter(private val name: String) { return params } + suspend fun executeSuspend(cls: Class): Flow { + return VKApi.suspendExecute(getSignedUrl(), cls) + } + fun execute(cls: Class): ArrayList? { return VKApi.execute(getSignedUrl(), cls) } @@ -96,11 +100,11 @@ open class MethodSetter(private val name: String) { } fun userIds(vararg ids: Int): MethodSetter { - return put("user_ids", ArrayUtils.asString(ids)) + return put("user_ids", com.meloda.fast.util.ArrayUtils.asString(ids)) } fun userIds(ids: ArrayList): MethodSetter { - return put("user_ids", ArrayUtils.asString(ids)) + return put("user_ids", com.meloda.fast.util.ArrayUtils.asString(ids)) } fun ownerId(value: Int): MethodSetter { @@ -112,11 +116,11 @@ open class MethodSetter(private val name: String) { } fun groupIds(vararg ids: Int): MethodSetter { - return put("group_ids", ArrayUtils.asString(ids)) + return put("group_ids", com.meloda.fast.util.ArrayUtils.asString(ids)) } fun groupIds(ids: ArrayList): MethodSetter { - return put("group_ids", ArrayUtils.asString(ids)) + return put("group_ids", com.meloda.fast.util.ArrayUtils.asString(ids)) } fun fields(values: String): MethodSetter { diff --git a/vksdk/src/main/java/com/meloda/vksdk/method/UserMethodSetter.kt b/app/src/main/kotlin/com/meloda/fast/api/method/UserMethodSetter.kt similarity index 96% rename from vksdk/src/main/java/com/meloda/vksdk/method/UserMethodSetter.kt rename to app/src/main/kotlin/com/meloda/fast/api/method/UserMethodSetter.kt index 872be9cc..5a8daa90 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/method/UserMethodSetter.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/method/UserMethodSetter.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.method +package com.meloda.fast.api.method class UserMethodSetter(name: String) : MethodSetter(name) { diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/ApiResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/model/ApiResponse.kt new file mode 100644 index 00000000..43ef6478 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/model/ApiResponse.kt @@ -0,0 +1,12 @@ +package com.meloda.fast.api.model + +data class ApiResponse constructor( + val isSuccessful: Boolean, + val error: Error?, + val response: T? +) + +data class Error constructor( + val code: Long, + val message: String +) \ No newline at end of file diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKAttachments.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt similarity index 98% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKAttachments.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt index c08d7177..4926a368 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKAttachments.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKAttachments.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONArray import java.util.* diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKAudio.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt similarity index 95% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKAudio.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt index c2320992..c2627477 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKAudio.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudio.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKAudioMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt similarity index 95% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKAudioMessage.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt index f7214e36..8e5321b3 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKAudioMessage.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKAudioMessage.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKCall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt similarity index 96% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKCall.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt index cc0ca05d..599e6769 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKCall.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKCall.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKComment.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt similarity index 89% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKComment.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt index c8e65590..eede04e3 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKComment.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKComment.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKConversation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt similarity index 94% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKConversation.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt index c97381d2..7867b8de 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKConversation.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKConversation.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject @@ -39,7 +39,7 @@ class VKConversation() : VKModel(), Cloneable { var isNoSound: Boolean = false var membersCount: Int = 0 - var title: String = "" + var title: String? = null var pinnedMessage: VKMessage? = null @@ -84,6 +84,7 @@ class VKConversation() : VKModel(), Cloneable { o.optJSONObject("chat_settings")?.let { membersCount = it.optInt("members_count") title = it.optString("title") + if (title?.isBlank() == true) title = null it.optJSONObject("pinned_message")?.let { pinned -> pinnedMessage = VKMessage(pinned) @@ -109,13 +110,9 @@ class VKConversation() : VKModel(), Cloneable { fun isGroup() = type == Type.GROUP - override fun toString(): String { - return title - } + override fun toString() = title ?: "" - public override fun clone(): VKConversation { - return super.clone() as VKConversation - } + public override fun clone() = super.clone() as VKConversation enum class Type(val value: String) { NULL("null"), diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKDocument.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt similarity index 98% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKDocument.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt index acd36156..7f059891 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKDocument.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKDocument.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject import java.io.Serializable diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKGeolocation.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt similarity index 88% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKGeolocation.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt index 288b362c..a4c8070d 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKGeolocation.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKGeolocation.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKGift.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt similarity index 93% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKGift.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt index 7392ad77..d18bf4d2 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKGift.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKGift.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKGraffiti.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt similarity index 94% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKGraffiti.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt index 473e5f72..99dfaf69 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKGraffiti.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKGraffiti.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKGroup.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt similarity index 97% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKGroup.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt index d64d9ae1..f35c8bab 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKGroup.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKGroup.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONArray import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKLink.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt similarity index 97% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKLink.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt index d4a2c62e..e457be49 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKLink.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKLink.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject import java.io.Serializable diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollHistory.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt similarity index 91% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollHistory.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt index 9bc45a8a..81a5be50 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollHistory.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollHistory.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import java.util.* diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollServer.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt similarity index 91% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollServer.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt index f55abfb9..64e39695 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKLongPollServer.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKLongPollServer.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKMessage.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt similarity index 98% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKMessage.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt index b3b11d70..e02555d1 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKMessage.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessage.kt @@ -1,7 +1,7 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import android.util.ArrayMap -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.api.util.VKUtil import org.json.JSONObject open class VKMessage() : VKModel() { diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKMessageAction.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt similarity index 97% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKMessageAction.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt index d2e071a0..241f671d 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKMessageAction.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKMessageAction.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKModel.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt similarity index 54% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKModel.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt index 2483cfef..e49eef52 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKModel.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKModel.kt @@ -1,8 +1,9 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model +import com.meloda.fast.base.adapter.BaseItem import java.io.Serializable -abstract class VKModel : Serializable { +abstract class VKModel : BaseItem(), Serializable { abstract val attachmentType: VKAttachments.Type diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKPhoto.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt similarity index 96% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKPhoto.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt index 531c72a1..363ab9be 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKPhoto.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhoto.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject import java.util.* diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKPhotoSize.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt similarity index 91% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKPhotoSize.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt index efdc4ae1..f7f1d8d6 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKPhotoSize.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKPhotoSize.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKPoll.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt similarity index 97% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKPoll.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt index a6b1895d..1881e108 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKPoll.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKPoll.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKSticker.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt similarity index 96% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKSticker.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt index 401f2b69..0064f51d 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKSticker.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKSticker.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject import java.util.* diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKUser.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt similarity index 98% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKUser.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt index 4e8cff17..9500856e 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKUser.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKUser.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONArray import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKVideo.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt similarity index 97% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKVideo.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt index 687aa006..3421bb2f 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKVideo.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKVideo.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/vksdk/src/main/java/com/meloda/vksdk/model/VKWall.kt b/app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt similarity index 89% rename from vksdk/src/main/java/com/meloda/vksdk/model/VKWall.kt rename to app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt index 0aa817c1..fc1724f9 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/model/VKWall.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/model/VKWall.kt @@ -1,4 +1,4 @@ -package com.meloda.vksdk.model +package com.meloda.fast.api.model import org.json.JSONObject diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/request/RequestMessagesGetConversations.kt b/app/src/main/kotlin/com/meloda/fast/api/model/request/RequestMessagesGetConversations.kt new file mode 100644 index 00000000..17f4d2ca --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/model/request/RequestMessagesGetConversations.kt @@ -0,0 +1,21 @@ +package com.meloda.fast.api.model.request + +import com.google.gson.annotations.SerializedName + +class RequestMessagesGetConversations( + @SerializedName("offset") + private val offset: Int = 0, + + @SerializedName("count") + private val count: Int = 0, + + //values = all, unread + @SerializedName("filter") + private val filter: String = "", + + @SerializedName("extended") + private val extended: Boolean = false, + + @SerializedName("fields") + private var fields: String = "" +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/api/model/response/MessagesResponse.kt b/app/src/main/kotlin/com/meloda/fast/api/model/response/MessagesResponse.kt new file mode 100644 index 00000000..46d10bdf --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/model/response/MessagesResponse.kt @@ -0,0 +1,13 @@ +package com.meloda.fast.api.model.response + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +class MessagesResponse( + val count: Int +) { +} + +@Parcelize +data class GetConversationsResponse(val a: String) : Parcelable +// TODO: 7/12/2021 use hilt for this like in LIR and make simple conversations' screen diff --git a/app/src/main/kotlin/com/meloda/fast/api/service/MessagesService.kt b/app/src/main/kotlin/com/meloda/fast/api/service/MessagesService.kt new file mode 100644 index 00000000..28b12721 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/api/service/MessagesService.kt @@ -0,0 +1,13 @@ +package com.meloda.fast.api.service + +import com.meloda.fast.api.model.ApiResponse +import com.meloda.fast.api.model.request.RequestMessagesGetConversations +import retrofit2.http.GET +import retrofit2.http.QueryMap + +interface MessagesService { + + @GET("messages.getConversations") + suspend fun getConversations(@QueryMap params: RequestMessagesGetConversations): ApiResponse> + +} \ No newline at end of file diff --git a/vksdk/src/main/java/com/meloda/vksdk/util/VKUtil.kt b/app/src/main/kotlin/com/meloda/fast/api/util/VKUtil.kt similarity index 91% rename from vksdk/src/main/java/com/meloda/vksdk/util/VKUtil.kt rename to app/src/main/kotlin/com/meloda/fast/api/util/VKUtil.kt index d4a009f1..09200155 100644 --- a/vksdk/src/main/java/com/meloda/vksdk/util/VKUtil.kt +++ b/app/src/main/kotlin/com/meloda/fast/api/util/VKUtil.kt @@ -1,7 +1,7 @@ -package com.meloda.vksdk.util +package com.meloda.fast.api.util import androidx.annotation.WorkerThread -import com.meloda.vksdk.model.* +import com.meloda.fast.api.model.* import org.json.JSONArray import org.json.JSONObject import java.text.SimpleDateFormat @@ -122,25 +122,29 @@ object VKUtil { } - fun getTitle(conversation: VKConversation, peerUser: VKUser?, peerGroup: VKGroup?): String { + fun getTitle( + conversation: VKConversation, + peerUser: VKUser?, + peerGroup: VKGroup? + ): String { return when { - conversation.isUser() -> { - peerUser?.let { return it.toString() } ?: "" - } + conversation.isUser() -> peerUser?.let { return it.toString() } ?: "" - conversation.isGroup() -> { - peerGroup?.let { return it.name } ?: "" - } - conversation.isChat() -> { - conversation.title - } + conversation.isGroup() -> peerGroup?.let { return it.name } ?: "" + + + conversation.isChat() -> conversation.title ?: "" else -> "" } } - fun getMessageTitle(message: VKMessage, fromUser: VKUser?, fromGroup: VKGroup?): String { + fun getMessageTitle( + message: VKMessage, + fromUser: VKUser?, + fromGroup: VKGroup? + ): String { return when { message.isFromUser() -> { fromUser?.let { return it.toString() } ?: "" @@ -154,7 +158,11 @@ object VKUtil { } } - fun getAvatar(conversation: VKConversation, peerUser: VKUser?, peerGroup: VKGroup?): String { + fun getAvatar( + conversation: VKConversation, + peerUser: VKUser?, + peerGroup: VKGroup? + ): String { return when { conversation.isUser() -> { peerUser?.let { return it.photo200 } ?: "" @@ -172,7 +180,11 @@ object VKUtil { } } - fun getUserAvatar(message: VKMessage, fromUser: VKUser?, fromGroup: VKGroup?): String { + fun getUserAvatar( + message: VKMessage, + fromUser: VKUser?, + fromGroup: VKGroup? + ): String { return when { message.isFromUser() -> { fromUser?.let { return it.photo100 } ?: "" @@ -278,7 +290,8 @@ object VKUtil { if (it.has("source_act")) { message.action = VKMessageAction().also { action -> - action.type = VKMessageAction.Type.fromString(it.optString("source_act")) + action.type = + VKMessageAction.Type.fromString(it.optString("source_act")) when (action.type) { VKMessageAction.Type.CHAT_CREATE -> { diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt new file mode 100644 index 00000000..1c910dad --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/BaseActivity.kt @@ -0,0 +1,48 @@ +package com.meloda.fast.base + +import android.os.Bundle +import android.view.View +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import com.google.android.material.snackbar.Snackbar + +abstract class BaseActivity : AppCompatActivity, LifecycleOwner { + + constructor() : super() + + constructor(@LayoutRes resId: Int) : super(resId) + + protected lateinit var lifecycleRegistry: LifecycleRegistry + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + lifecycleRegistry = LifecycleRegistry(this) + lifecycleRegistry.currentState = Lifecycle.State.CREATED + } + + override fun onStart() { + super.onStart() + lifecycleRegistry.currentState = Lifecycle.State.STARTED + } + + override fun onResume() { + super.onResume() + lifecycleRegistry.currentState = Lifecycle.State.RESUMED + } + + override fun onDestroy() { + super.onDestroy() + lifecycleRegistry.currentState = Lifecycle.State.DESTROYED + } + + val rootView: View? get() = findViewById(android.R.id.content) + + fun requireRootView() = rootView!! + + var errorSnackbar: Snackbar? = null + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt new file mode 100644 index 00000000..879bbeeb --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/BaseFragment.kt @@ -0,0 +1,12 @@ +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) + +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/BaseFullscreenDialog.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseFullscreenDialog.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/base/BaseFullscreenDialog.kt rename to app/src/main/kotlin/com/meloda/fast/base/BaseFullscreenDialog.kt diff --git a/app/src/main/kotlin/com/meloda/fast/base/BaseVMFragment.kt b/app/src/main/kotlin/com/meloda/fast/base/BaseVMFragment.kt new file mode 100644 index 00000000..e7c5de1c --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/BaseVMFragment.kt @@ -0,0 +1,29 @@ +package com.meloda.fast.base + +import android.os.Bundle +import android.view.View +import androidx.annotation.LayoutRes +import androidx.lifecycle.lifecycleScope +import com.meloda.fast.base.viewmodel.BaseVM +import com.meloda.fast.base.viewmodel.VKEvent +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach + +abstract class BaseVMFragment : 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) + viewLifecycleOwner.lifecycleScope.launchWhenStarted { + viewModel.tasksEvent.onEach { onEvent(it) }.collect() + } + } + + protected open fun onEvent(event: VKEvent) {} + +} \ No newline at end of file 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 new file mode 100644 index 00000000..5b922c2b --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseAdapter.kt @@ -0,0 +1,132 @@ +package com.meloda.fast.base.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter + +@Suppress("UNCHECKED_CAST", "unused", "MemberVisibilityCanBePrivate", "CanBeParameter") +abstract class BaseAdapter( + var context: Context, + values: ArrayList, + diffUtil: DiffUtil.ItemCallback +) : ListAdapter(diffUtil) { + + val cleanValues = arrayListOf() + val values = arrayListOf() + + init { + addAll(values) + } + + protected var inflater: LayoutInflater = LayoutInflater.from(context) + + var itemClickListener: OnItemClickListener? = null + var itemLongClickListener: OnItemLongClickListener? = null + + open fun destroy() { + itemClickListener = null + itemLongClickListener = null + } + + override fun getItem(position: Int): Item { + return values[position] + } + + fun add(position: Int, item: Item) { + values.add(position, item) + cleanValues.add(position, item) + } + + fun add(item: Item) { + values += item + cleanValues.add(item) + } + + fun addAll(items: List) { + values += items + cleanValues.addAll(items) + } + + fun addAll(position: Int, items: List) { + values.addAll(position, items) + cleanValues.addAll(position, items) + } + + fun removeAll(items: List) { + values.removeAll(items) + cleanValues.removeAll(items) + } + + fun removeAt(index: Int) { + values.removeAt(index) + cleanValues.removeAt(index) + } + + fun remove(item: Item) { + values.remove(item) + cleanValues.remove(item) + } + + fun clear() { + values.clear() + cleanValues.clear() + } + + operator fun get(position: Int): Item { + return values[position] + } + + operator fun set(position: Int, item: Item) { + values[position] = item + cleanValues[position] = item + } + + open fun notifyChanges(oldList: List, newList: List) {} + + fun isEmpty() = values.isEmpty() + fun isNotEmpty() = values.isNotEmpty() + + fun view(resId: Int, viewGroup: ViewGroup, attachToRoot: Boolean = false): View { + return inflater.inflate(resId, viewGroup, attachToRoot) + } + + fun updateValues(arrayList: ArrayList) { + values.clear() + values += arrayList + } + + fun updateValues(list: List) = updateValues(ArrayList(list)) + + override fun onBindViewHolder(holder: VH, position: Int) { + onBindItemViewHolder(holder, position) + } + + protected fun initListeners(itemView: View, position: Int) { + if (itemView is AdapterView<*>) return + + itemView.setOnClickListener { + itemClickListener?.onItemClick(position) + } + + itemView.setOnLongClickListener { + itemLongClickListener?.onItemLongClick(position) + return@setOnLongClickListener itemClickListener == null + } + } + + override fun getItemCount(): Int { + return values.size + } + + val size get() = itemCount + + private fun onBindItemViewHolder(holder: VH, position: Int) { + initListeners(holder.itemView, position) + holder.bind(position) + } + +} diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseItem.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseItem.kt new file mode 100644 index 00000000..7b1630b1 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/BaseItem.kt @@ -0,0 +1,3 @@ +package com.meloda.fast.base.adapter + +abstract class BaseItem \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/base/adapter/EmptyHeaderAdapter.kt b/app/src/main/kotlin/com/meloda/fast/base/adapter/EmptyHeaderAdapter.kt new file mode 100644 index 00000000..6da60751 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/EmptyHeaderAdapter.kt @@ -0,0 +1,35 @@ +package com.meloda.fast.base.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isInvisible +import androidx.recyclerview.widget.RecyclerView +import com.meloda.fast.util.AndroidUtils +import kotlin.math.roundToInt + +class EmptyHeaderAdapter( + var context: Context +) : RecyclerView.Adapter() { + + inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = Holder(generateHeaderView()) + + override fun onBindViewHolder(holder: Holder, position: Int) { + } + + override fun getItemCount() = 1 + + private fun generateHeaderView() = View(context).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + AndroidUtils.px(56).roundToInt() + ) + isClickable = false + isEnabled = false + isFocusable = false + isInvisible = true + } + +} \ 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 new file mode 100644 index 00000000..eb6989ba --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/Holders.kt @@ -0,0 +1,15 @@ +package com.meloda.fast.base.adapter + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding + +abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) { + + open fun bind(position: Int) {} + + open fun bind(position: Int, payloads: MutableList?) {} + +} + +abstract class BindingHolder(protected val binding: B) : BaseHolder(binding.root) \ 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 new file mode 100644 index 00000000..5fdbccb7 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/adapter/Listeners.kt @@ -0,0 +1,9 @@ +package com.meloda.fast.base.adapter + +interface OnItemClickListener { + fun onItemClick(position: Int) +} + +interface OnItemLongClickListener { + fun onItemLongClick(position: Int) +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseVM.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseVM.kt new file mode 100644 index 00000000..69b4a8f5 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/BaseVM.kt @@ -0,0 +1,33 @@ +package com.meloda.fast.base.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.meloda.fast.api.Answer +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch + +abstract class BaseVM : ViewModel() { + + protected val tasksEventChannel = Channel() + val tasksEvent = tasksEventChannel.receiveAsFlow() + + protected fun makeJob( + job: suspend () -> Answer, + onAnswer: suspend (T) -> Unit = {}, + onStart: (suspend () -> Unit)? = null, + onEnd: (suspend () -> Unit)? = null, + onError: (suspend (String) -> Unit)? = null + ) = viewModelScope.launch { + onStart?.invoke() + when (val response = job()) { + is Answer.Success -> onAnswer(response.data) + is Answer.Error -> onError?.invoke(response.errorString) ?: tasksEventChannel.send( + ShowDialogInfoEvent(null, response.errorString) + ) + } + }.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } } + + protected suspend fun sendEvent(event: T) = tasksEventChannel.send(event) + +} \ No newline at end of file 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 new file mode 100644 index 00000000..dfa637b3 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/Events.kt @@ -0,0 +1,11 @@ +package com.meloda.fast.base.viewmodel + +data class ShowDialogInfoEvent( + val title: String? = null, + val message: String, + val positiveBtn: String? = null, + val negativeBtn: String? = null +) : VKEvent() + +object StartProgressEvent : VKEvent() +object StopProgressEvent : VKEvent() \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/base/viewmodel/VKEvent.kt b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/VKEvent.kt new file mode 100644 index 00000000..34d800a9 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/base/viewmodel/VKEvent.kt @@ -0,0 +1,3 @@ +package com.meloda.fast.base.viewmodel + +abstract class VKEvent \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt similarity index 51% rename from app/src/main/java/com/meloda/fast/common/AppGlobal.kt rename to app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt index 69af67ef..4f79d1cf 100644 --- a/app/src/main/java/com/meloda/fast/common/AppGlobal.kt +++ b/app/src/main/kotlin/com/meloda/fast/common/AppGlobal.kt @@ -2,32 +2,20 @@ package com.meloda.fast.common import android.annotation.SuppressLint import android.app.Application -import android.app.DownloadManager -import android.content.ClipboardManager import android.content.Context import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.res.Resources import android.database.sqlite.SQLiteDatabase -import android.net.ConnectivityManager import android.os.Handler -import android.view.WindowManager import android.view.inputmethod.InputMethodManager -import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.pm.PackageInfoCompat import androidx.preference.PreferenceManager -import com.facebook.drawee.backends.pipeline.Fresco import com.meloda.fast.BuildConfig import com.meloda.fast.R -import com.meloda.fast.UserConfig -import com.meloda.fast.database.CacheStorage import com.meloda.fast.database.DatabaseHelper -import com.meloda.fast.fragment.SettingsFragment import com.meloda.fast.util.AndroidUtils -import com.meloda.mvp.MvpBase -import com.microsoft.appcenter.AppCenter -import com.microsoft.appcenter.analytics.Analytics -import com.microsoft.appcenter.crashes.Crashes +import dagger.hilt.android.HiltAndroidApp import org.acra.ACRA import org.acra.ReportingInteractionMode import org.acra.annotation.ReportsCrashes @@ -43,16 +31,12 @@ import java.util.* resDialogPositiveButtonText = R.string.send_crash_report, resDialogNegativeButtonText = R.string.ok ) +@HiltAndroidApp class AppGlobal : Application() { companion object { - const val APP_CENTER_TOKEN = "c87e410a-d622-4c52-ad7e-7388ab511704" - lateinit var windowManager: WindowManager - lateinit var connectivityManager: ConnectivityManager lateinit var inputMethodManager: InputMethodManager - lateinit var clipboardManager: ClipboardManager - lateinit var downloadManager: DownloadManager lateinit var preferences: SharedPreferences lateinit var locale: Locale @@ -82,15 +66,9 @@ class AppGlobal : Application() { instance = this if (!BuildConfig.DEBUG) { - AppCenter.start( - this, APP_CENTER_TOKEN, Analytics::class.java, Crashes::class.java - ) - ACRA.init(this) } - Fresco.initialize(this) - preferences = PreferenceManager.getDefaultSharedPreferences(this) handler = Handler(mainLooper) locale = Locale.getDefault() @@ -106,62 +84,10 @@ class AppGlobal : Application() { Companion.packageName = packageName Companion.packageManager = packageManager - inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager - connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager - screenWidth = AndroidUtils.getDisplayWidth() screenHeight = AndroidUtils.getDisplayHeight() - UserConfig.restore() - - MvpBase.init(handler) - - logDatabase() - fillMemoryCache() - - applyNightMode() - } - - private fun logDatabase() { - val users = CacheStorage.usersStorage.getAllValues() - val groups = CacheStorage.groupsStorage.getAllValues() - val chats = CacheStorage.chatsStorage.getAllValues() - val messages = CacheStorage.messagesStorage.getAllValues() - - return - } - - private fun fillMemoryCache() { - - } - - fun applyNightMode(value: String? = null): Int { - val mode = value ?: preferences.getString(SettingsFragment.KEY_THEME, "-1")!! - - val nightMode = getNightMode(mode.toInt()) - - val oldNightMode = AppCompatDelegate.getDefaultNightMode() - - AppCompatDelegate.setDefaultNightMode(nightMode) - - return nightMode - } - - fun getNightMode(nightMode: Int = -1): Int { - val mode = if (nightMode != -1) nightMode else preferences.getString( - SettingsFragment.KEY_THEME, - "-1" - )!!.toInt() - - return when (mode) { - 1 -> AppCompatDelegate.MODE_NIGHT_YES - 2 -> AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY - 3 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - else -> AppCompatDelegate.MODE_NIGHT_NO - } + inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager } } \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/common/FragmentSwitcher.kt b/app/src/main/kotlin/com/meloda/fast/common/FragmentSwitcher.kt similarity index 84% rename from app/src/main/java/com/meloda/fast/common/FragmentSwitcher.kt rename to app/src/main/kotlin/com/meloda/fast/common/FragmentSwitcher.kt index adfbd0f8..07a63ede 100644 --- a/app/src/main/java/com/meloda/fast/common/FragmentSwitcher.kt +++ b/app/src/main/kotlin/com/meloda/fast/common/FragmentSwitcher.kt @@ -3,8 +3,6 @@ package com.meloda.fast.common import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import com.meloda.fast.R -import com.meloda.fast.fragment.FragmentConversationsDeprecated -import com.meloda.fast.fragment.FragmentFriendsDeprecated object FragmentSwitcher { @@ -62,8 +60,7 @@ object FragmentSwitcher { val transaction = fragmentManager.beginTransaction() if (fragmentToShow == null) { - fragmentToShow = createFragmentByTag(tag) - transaction.add(containerId, fragmentToShow, tag) + throw NullPointerException("Required fragment is null") } else { transaction.show(fragmentToShow) } @@ -105,12 +102,4 @@ object FragmentSwitcher { transaction.commitNow() } - - private fun createFragmentByTag(tag: String): Fragment { - return when (tag) { - "FragmentFriends" -> FragmentFriendsDeprecated() - "FragmentConversations" -> FragmentConversationsDeprecated() - else -> Fragment() - } - } } diff --git a/app/src/main/java/com/meloda/fast/common/TimeManager.kt b/app/src/main/kotlin/com/meloda/fast/common/TimeManager.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/common/TimeManager.kt rename to app/src/main/kotlin/com/meloda/fast/common/TimeManager.kt diff --git a/app/src/main/java/com/meloda/fast/common/UpdateManager.kt b/app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt similarity index 96% rename from app/src/main/java/com/meloda/fast/common/UpdateManager.kt rename to app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt index 1d7669bf..759aefc7 100644 --- a/app/src/main/java/com/meloda/fast/common/UpdateManager.kt +++ b/app/src/main/kotlin/com/meloda/fast/common/UpdateManager.kt @@ -2,10 +2,10 @@ package com.meloda.fast.common import android.util.Log import androidx.collection.arrayMapOf -import com.meloda.concurrent.TaskManager +import com.meloda.fast.concurrent.TaskManager import com.meloda.fast.BuildConfig import com.meloda.fast.model.NewUpdateInfo -import com.meloda.netservices.HttpRequest +import com.meloda.fast.net.HttpRequest import org.json.JSONArray import org.json.JSONObject diff --git a/concurrent/src/main/java/com/meloda/concurrent/EventInfo.kt b/app/src/main/kotlin/com/meloda/fast/concurrent/EventInfo.kt similarity index 66% rename from concurrent/src/main/java/com/meloda/concurrent/EventInfo.kt rename to app/src/main/kotlin/com/meloda/fast/concurrent/EventInfo.kt index 2c5416ad..5b975967 100644 --- a/concurrent/src/main/java/com/meloda/concurrent/EventInfo.kt +++ b/app/src/main/kotlin/com/meloda/fast/concurrent/EventInfo.kt @@ -1,3 +1,3 @@ -package com.meloda.concurrent +package com.meloda.fast.concurrent class EventInfo constructor(var key: String, var data: T? = null) \ No newline at end of file diff --git a/concurrent/src/main/java/com/meloda/concurrent/LowThread.kt b/app/src/main/kotlin/com/meloda/fast/concurrent/LowThread.kt similarity index 85% rename from concurrent/src/main/java/com/meloda/concurrent/LowThread.kt rename to app/src/main/kotlin/com/meloda/fast/concurrent/LowThread.kt index be593a2b..13b1c214 100644 --- a/concurrent/src/main/java/com/meloda/concurrent/LowThread.kt +++ b/app/src/main/kotlin/com/meloda/fast/concurrent/LowThread.kt @@ -1,4 +1,4 @@ -package com.meloda.concurrent +package com.meloda.fast.concurrent import android.os.Process diff --git a/concurrent/src/main/java/com/meloda/concurrent/TaskManager.kt b/app/src/main/kotlin/com/meloda/fast/concurrent/TaskManager.kt similarity index 94% rename from concurrent/src/main/java/com/meloda/concurrent/TaskManager.kt rename to app/src/main/kotlin/com/meloda/fast/concurrent/TaskManager.kt index f8a25e6a..6629f087 100644 --- a/concurrent/src/main/java/com/meloda/concurrent/TaskManager.kt +++ b/app/src/main/kotlin/com/meloda/fast/concurrent/TaskManager.kt @@ -1,4 +1,4 @@ -package com.meloda.concurrent +package com.meloda.fast.concurrent object TaskManager { diff --git a/app/src/main/java/com/meloda/fast/database/CacheStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt similarity index 96% rename from app/src/main/java/com/meloda/fast/database/CacheStorage.kt rename to app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt index 2b1d9afe..3386f697 100644 --- a/app/src/main/java/com/meloda/fast/database/CacheStorage.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/CacheStorage.kt @@ -12,9 +12,9 @@ import com.meloda.fast.database.storage.ChatsStorage import com.meloda.fast.database.storage.GroupsStorage import com.meloda.fast.database.storage.MessagesStorage import com.meloda.fast.database.storage.UsersStorage -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.model.VKUser +import com.meloda.fast.api.model.VKConversation +import com.meloda.fast.api.model.VKMessage +import com.meloda.fast.api.model.VKUser import java.util.* object CacheStorage { diff --git a/app/src/main/java/com/meloda/fast/database/DatabaseHelper.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseHelper.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/database/DatabaseHelper.kt rename to app/src/main/kotlin/com/meloda/fast/database/DatabaseHelper.kt diff --git a/app/src/main/java/com/meloda/fast/database/DatabaseKeys.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseKeys.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/database/DatabaseKeys.kt rename to app/src/main/kotlin/com/meloda/fast/database/DatabaseKeys.kt diff --git a/app/src/main/java/com/meloda/fast/database/DatabaseUtils.kt b/app/src/main/kotlin/com/meloda/fast/database/DatabaseUtils.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/database/DatabaseUtils.kt rename to app/src/main/kotlin/com/meloda/fast/database/DatabaseUtils.kt diff --git a/app/src/main/java/com/meloda/fast/database/QueryBuilder.kt b/app/src/main/kotlin/com/meloda/fast/database/QueryBuilder.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/database/QueryBuilder.kt rename to app/src/main/kotlin/com/meloda/fast/database/QueryBuilder.kt diff --git a/app/src/main/java/com/meloda/fast/database/base/Storage.kt b/app/src/main/kotlin/com/meloda/fast/database/base/Storage.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/database/base/Storage.kt rename to app/src/main/kotlin/com/meloda/fast/database/base/Storage.kt diff --git a/app/src/main/java/com/meloda/fast/database/storage/ChatsStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt similarity index 98% rename from app/src/main/java/com/meloda/fast/database/storage/ChatsStorage.kt rename to app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt index 4d906eed..3db39d94 100644 --- a/app/src/main/java/com/meloda/fast/database/storage/ChatsStorage.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/storage/ChatsStorage.kt @@ -25,8 +25,8 @@ import com.meloda.fast.database.DatabaseKeys.TYPE import com.meloda.fast.database.DatabaseKeys.UNREAD_COUNT import com.meloda.fast.database.DatabaseUtils.TABLE_CHATS import com.meloda.fast.database.base.Storage -import com.meloda.vksdk.model.VKConversation -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.api.model.VKConversation +import com.meloda.fast.api.util.VKUtil import org.json.JSONObject @WorkerThread diff --git a/app/src/main/java/com/meloda/fast/database/storage/GroupsStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt similarity index 97% rename from app/src/main/java/com/meloda/fast/database/storage/GroupsStorage.kt rename to app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt index 93810b28..4ba14d45 100644 --- a/app/src/main/java/com/meloda/fast/database/storage/GroupsStorage.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/storage/GroupsStorage.kt @@ -17,8 +17,8 @@ import com.meloda.fast.database.DatabaseKeys.SCREEN_NAME import com.meloda.fast.database.DatabaseKeys.TYPE import com.meloda.fast.database.DatabaseUtils.TABLE_GROUPS import com.meloda.fast.database.base.Storage -import com.meloda.vksdk.model.VKGroup -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.api.model.VKGroup +import com.meloda.fast.api.util.VKUtil import org.json.JSONObject class GroupsStorage : Storage() { diff --git a/app/src/main/java/com/meloda/fast/database/storage/MessagesStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt similarity index 97% rename from app/src/main/java/com/meloda/fast/database/storage/MessagesStorage.kt rename to app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt index a9203e61..02f62e36 100644 --- a/app/src/main/java/com/meloda/fast/database/storage/MessagesStorage.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/storage/MessagesStorage.kt @@ -23,9 +23,9 @@ import com.meloda.fast.database.DatabaseKeys.TEXT import com.meloda.fast.database.DatabaseUtils.TABLE_MESSAGES import com.meloda.fast.database.base.Storage import com.meloda.fast.util.Utils -import com.meloda.vksdk.model.VKMessage -import com.meloda.vksdk.model.VKMessageAction -import com.meloda.vksdk.model.VKModel +import com.meloda.fast.api.model.VKMessage +import com.meloda.fast.api.model.VKMessageAction +import com.meloda.fast.api.model.VKModel import java.util.stream.Collectors @WorkerThread diff --git a/app/src/main/java/com/meloda/fast/database/storage/UsersStorage.kt b/app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt similarity index 98% rename from app/src/main/java/com/meloda/fast/database/storage/UsersStorage.kt rename to app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt index f2296c7e..280d39f6 100644 --- a/app/src/main/java/com/meloda/fast/database/storage/UsersStorage.kt +++ b/app/src/main/kotlin/com/meloda/fast/database/storage/UsersStorage.kt @@ -25,8 +25,8 @@ import com.meloda.fast.database.DatabaseUtils.TABLE_FRIENDS import com.meloda.fast.database.DatabaseUtils.TABLE_USERS import com.meloda.fast.database.QueryBuilder import com.meloda.fast.database.base.Storage -import com.meloda.vksdk.model.VKUser -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.api.model.VKUser +import com.meloda.fast.api.util.VKUtil import org.json.JSONObject @WorkerThread diff --git a/extensions/src/main/java/com/meloda/extensions/ContextExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/ContextExtensions.kt similarity index 96% rename from extensions/src/main/java/com/meloda/extensions/ContextExtensions.kt rename to app/src/main/kotlin/com/meloda/fast/extensions/ContextExtensions.kt index b9062e33..dcce0bf6 100644 --- a/extensions/src/main/java/com/meloda/extensions/ContextExtensions.kt +++ b/app/src/main/kotlin/com/meloda/fast/extensions/ContextExtensions.kt @@ -1,4 +1,4 @@ -package com.meloda.extensions +package com.meloda.fast.extensions import android.content.Context import android.graphics.Typeface diff --git a/extensions/src/main/java/com/meloda/extensions/DrawableExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/DrawableExtensions.kt similarity index 86% rename from extensions/src/main/java/com/meloda/extensions/DrawableExtensions.kt rename to app/src/main/kotlin/com/meloda/fast/extensions/DrawableExtensions.kt index b83b4108..579e0442 100644 --- a/extensions/src/main/java/com/meloda/extensions/DrawableExtensions.kt +++ b/app/src/main/kotlin/com/meloda/fast/extensions/DrawableExtensions.kt @@ -1,4 +1,4 @@ -package com.meloda.extensions +package com.meloda.fast.extensions import android.graphics.drawable.Drawable import androidx.annotation.ColorInt diff --git a/extensions/src/main/java/com/meloda/extensions/FloatExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/FloatExtensions.kt similarity index 77% rename from extensions/src/main/java/com/meloda/extensions/FloatExtensions.kt rename to app/src/main/kotlin/com/meloda/fast/extensions/FloatExtensions.kt index da48f4f2..42fdca5e 100644 --- a/extensions/src/main/java/com/meloda/extensions/FloatExtensions.kt +++ b/app/src/main/kotlin/com/meloda/fast/extensions/FloatExtensions.kt @@ -1,4 +1,4 @@ -package com.meloda.extensions +package com.meloda.fast.extensions import kotlin.math.roundToInt diff --git a/app/src/main/kotlin/com/meloda/fast/extensions/LiveDataExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/LiveDataExtensions.kt new file mode 100644 index 00000000..552855be --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/extensions/LiveDataExtensions.kt @@ -0,0 +1,116 @@ +package com.meloda.fast.extensions + +import androidx.annotation.UiThread +import androidx.lifecycle.MutableLiveData + +object LiveDataExtensions { + + operator fun MutableLiveData>.set(position: Int, v: T) { + val value = (this.value ?: arrayListOf()).apply { this[position] = v } + this.value = value + } + + operator fun MutableLiveData>.get(position: Int): T { + return (value as MutableList)[position] + } + + @JvmOverloads + fun MutableLiveData>.add(v: T, position: Int = -1) { + val value = (this.value ?: arrayListOf()).apply { + if (position == -1) this.add(v) else this.add(position, v) + } + + this.value = value + } + + @JvmOverloads + fun MutableLiveData>.addAll(values: List, position: Int = -1) { + val value = (this.value ?: arrayListOf()).apply { + if (position == -1) this.addAll(values) + else this.addAll(position, values) + } + + this.value = value + } + + @Suppress("TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING") + fun MutableLiveData>.removeAll(values: List) { + val value = (this.value ?: arrayListOf()).apply { + this.removeAll(values) + } + + this.value = value + } + + fun MutableLiveData>.removeAt(index: Int) { + val value = (this.value ?: arrayListOf()).apply { + this.removeAt(index) + } + + this.value = value + } + + fun MutableLiveData>.remove(item: T) { + val value = (this.value ?: arrayListOf()).apply { + this.remove(item) + } + + this.value = value + } + + operator fun MutableLiveData>.iterator(): Iterator { + return (value as MutableList).iterator() + } + + fun MutableLiveData>.clear() { + value = arrayListOf() + } + + val MutableLiveData>.indices get() = (value as MutableList).indices + + val MutableLiveData>.size get() = (value as MutableList).size + + fun MutableLiveData>.isEmpty(): Boolean { + return (value as MutableList).isEmpty() + } + + fun MutableLiveData>.isNotEmpty(): Boolean { + return !isEmpty() + } + + fun MutableLiveData>.requireValue() = value!! + + @UiThread + operator fun MutableLiveData>.plusAssign(values: List) { + val value = (this.value ?: arrayListOf()).apply { + this.addAll(values) + } + + this.value = value + } + + operator fun MutableLiveData>.plusAssign(v: T) { + val value = (this.value ?: arrayListOf()).apply { + this.add(v) + } + + this.value = value + } + + operator fun MutableLiveData>.minusAssign(values: List) { + val value = (this.value ?: arrayListOf()).apply { + this.removeAll(values) + } + + this.value = value + } + + operator fun MutableLiveData>.minusAssign(v: T) { + val value = (this.value ?: arrayListOf()).apply { + this.remove(v) + } + + this.value = value + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/extensions/NavigationExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/NavigationExtensions.kt new file mode 100644 index 00000000..ad2b3c72 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/extensions/NavigationExtensions.kt @@ -0,0 +1,265 @@ +package com.meloda.fast.extensions + +import android.content.Intent +import android.util.SparseArray +import androidx.core.util.forEach +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment +import com.google.android.material.bottomnavigation.BottomNavigationView +import com.meloda.fast.R +import com.meloda.fast.UserConfig + +/** + * Manages the various graphs needed for a [BottomNavigationView]. + * + * This sample is a workaround until the Navigation Component supports multiple back stacks. + */ +object NavigationExtensions { + + fun BottomNavigationView.setupWithNavController( + navGraphIds: List, + fragmentManager: FragmentManager, + containerId: Int, + intent: Intent + ): LiveData { + + // Map of tags + val graphIdToTagMap = SparseArray() + // Result. Mutable live data with the selected controlled + val selectedNavController = MutableLiveData() + + var firstFragmentGraphId = 0 + + // First create a NavHostFragment for each NavGraph ID + navGraphIds.forEachIndexed { index, navGraphId -> + val fragmentTag = getFragmentTag(index) + + // Find or create the Navigation host fragment + val navHostFragment = obtainNavHostFragment( + fragmentManager, + fragmentTag, + navGraphId, + containerId + ) + + // Obtain its id + val graphId = navHostFragment.navController.graph.id + + if (index == 0) { + firstFragmentGraphId = graphId + } + + // Save to the map + graphIdToTagMap[graphId] = fragmentTag + + // Attach or detach nav host fragment depending on whether it's the selected item. + if (this.selectedItemId == graphId) { + // Update livedata with the selected graph + selectedNavController.value = navHostFragment.navController + attachNavHostFragment(fragmentManager, navHostFragment, index == 0) + } else { + detachNavHostFragment(fragmentManager, navHostFragment) + } + } + + // Now connect selecting an item with swapping Fragments + var selectedItemTag = graphIdToTagMap[this.selectedItemId] + val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] + var isOnFirstFragment = selectedItemTag == firstFragmentTag + + setOnItemSelectedListener { item -> + // Don't do anything if the state is state has already been saved. + if (fragmentManager.isStateSaved) { + false + } else { + val navController = + (fragmentManager.findFragmentByTag(selectedItemTag) as NavHostFragment).navController + navController.popBackStack(navController.graph.startDestination, false) + if (selectedItemTag != graphIdToTagMap[item.itemId]) { + val newlySelectedItemTag = //graphIdToTagMap[item.itemId] + if (!UserConfig.isLoggedIn()) graphIdToTagMap[R.id.login] else graphIdToTagMap[item.itemId] + + fragmentManager.popBackStack( + firstFragmentTag, + FragmentManager.POP_BACK_STACK_INCLUSIVE + ) + val selectedFragment = + fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment + + // Exclude the first fragment tag because it's always in the back stack. + if (firstFragmentTag != newlySelectedItemTag) { + // Commit a transaction that cleans the back stack and adds the first fragment + // to it, creating the fixed started destination. + fragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.nav_default_enter_anim, + R.anim.nav_default_exit_anim, + R.anim.nav_default_pop_enter_anim, + R.anim.nav_default_pop_exit_anim + ) + .attach(selectedFragment) + .setPrimaryNavigationFragment(selectedFragment) + .apply { + // Detach all other Fragments + graphIdToTagMap.forEach { _, fragmentTagIter -> + if (fragmentTagIter != newlySelectedItemTag) { + detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) + } + } + } + .addToBackStack(firstFragmentTag) + .setReorderingAllowed(true) + .commit() + } + selectedItemTag = newlySelectedItemTag + isOnFirstFragment = selectedItemTag == firstFragmentTag + selectedNavController.value = selectedFragment.navController + true + } else { + false + } + } + } + + setOnItemReselectedListener { item -> + val newlySelectedItemTag = graphIdToTagMap[item.itemId] + val selectedFragment = + fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment + val navController = selectedFragment.navController + // Pop the back stack to the start destination of the current navController graph + if (selectedItemTag != graphIdToTagMap[item.itemId]) { + fragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.nav_default_enter_anim, + R.anim.nav_default_exit_anim, + R.anim.nav_default_pop_enter_anim, + R.anim.nav_default_pop_exit_anim + ) + .attach(selectedFragment) + .setPrimaryNavigationFragment(selectedFragment) + .apply { + // Detach all other Fragments + graphIdToTagMap.forEach { _, fragmentTagIter -> + if (fragmentTagIter != newlySelectedItemTag) { + detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) + } + } + } + .addToBackStack(firstFragmentTag) + .setReorderingAllowed(true) + .commit() + selectedItemTag = newlySelectedItemTag + isOnFirstFragment = selectedItemTag == firstFragmentTag + selectedNavController.value = selectedFragment.navController + } else navController.popBackStack(navController.graph.startDestination, false) + } + // Optional: on item reselected, pop back stack to the destination of the graph + setupDeepLinks(navGraphIds, fragmentManager, containerId, intent) + + // Finally, ensure that we update our BottomNavigationView when the back stack changes + fragmentManager.addOnBackStackChangedListener { + if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) { + this.selectedItemId = firstFragmentGraphId + } + + // Reset the graph if the currentDestination is not valid (happens when the back + // stack is popped after using the back button). + selectedNavController.value?.let { controller -> + if (controller.currentDestination == null) { + controller.navigate(controller.graph.id) + } + } + } + return selectedNavController + } + + private fun BottomNavigationView.setupDeepLinks( + navGraphIds: List, + fragmentManager: FragmentManager, + containerId: Int, + intent: Intent + ) { + navGraphIds.forEachIndexed { index, navGraphId -> + val fragmentTag = getFragmentTag(index) + + // Find or create the Navigation host fragment + val navHostFragment = obtainNavHostFragment( + fragmentManager, + fragmentTag, + navGraphId, + containerId + ) + // Handle Intent + if (navHostFragment.navController.handleDeepLink(intent) && + selectedItemId != navHostFragment.navController.graph.id + ) { + this.selectedItemId = navHostFragment.navController.graph.id + } + } + } + + private fun detachNavHostFragment( + fragmentManager: FragmentManager, + navHostFragment: NavHostFragment + ) { + fragmentManager.beginTransaction() + .detach(navHostFragment) + .commitNow() + } + + private fun attachNavHostFragment( + fragmentManager: FragmentManager, + navHostFragment: NavHostFragment, + isPrimaryNavFragment: Boolean + ) { + fragmentManager.beginTransaction() + .attach(navHostFragment) + .apply { + if (isPrimaryNavFragment) { + setPrimaryNavigationFragment(navHostFragment) + } + } + .commitNow() + } + + private fun obtainNavHostFragment( + fragmentManager: FragmentManager, + fragmentTag: String, + navGraphId: Int, + containerId: Int, + ): NavHostFragment { + // If the Nav Host fragment exists, return it + val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment? + existingFragment?.let { return it } + + // Otherwise, create it and return it. + val navHostFragment = NavHostFragment.create(navGraphId) + fragmentManager.beginTransaction() + .add(containerId, navHostFragment, fragmentTag) + .commitNow() + return navHostFragment + } + + private fun FragmentManager.isOnBackStack(backStackName: String): Boolean { + val backStackCount = backStackEntryCount + for (index in 0 until backStackCount) { + if (getBackStackEntryAt(index).name == backStackName) { + return true + } + } + return false + } + + val FragmentManager.visibleFragments + get(): List { + val visibleFragments = arrayListOf() + fragments.forEach { if (it.isVisible) visibleFragments.add(it) } + return visibleFragments + } + + private fun getFragmentTag(index: Int) = "bottomNavigation#$index" +} \ No newline at end of file diff --git a/extensions/src/main/java/com/meloda/extensions/StringExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/StringExtensions.kt similarity index 80% rename from extensions/src/main/java/com/meloda/extensions/StringExtensions.kt rename to app/src/main/kotlin/com/meloda/fast/extensions/StringExtensions.kt index fec51ee3..7d2bde6a 100644 --- a/extensions/src/main/java/com/meloda/extensions/StringExtensions.kt +++ b/app/src/main/kotlin/com/meloda/fast/extensions/StringExtensions.kt @@ -1,4 +1,4 @@ -package com.meloda.extensions +package com.meloda.fast.extensions import java.util.* diff --git a/extensions/src/main/java/com/meloda/extensions/TextViewExtensions.kt b/app/src/main/kotlin/com/meloda/fast/extensions/TextViewExtensions.kt similarity index 87% rename from extensions/src/main/java/com/meloda/extensions/TextViewExtensions.kt rename to app/src/main/kotlin/com/meloda/fast/extensions/TextViewExtensions.kt index 17d50848..9d130722 100644 --- a/extensions/src/main/java/com/meloda/extensions/TextViewExtensions.kt +++ b/app/src/main/kotlin/com/meloda/fast/extensions/TextViewExtensions.kt @@ -1,4 +1,4 @@ -package com.meloda.extensions +package com.meloda.fast.extensions import android.widget.TextView import com.google.android.material.textfield.TextInputLayout diff --git a/netservices/src/main/java/com/meloda/netservices/io/BytesOutputStream.kt b/app/src/main/kotlin/com/meloda/fast/io/BytesOutputStream.kt similarity index 85% rename from netservices/src/main/java/com/meloda/netservices/io/BytesOutputStream.kt rename to app/src/main/kotlin/com/meloda/fast/io/BytesOutputStream.kt index 89746909..65892969 100644 --- a/netservices/src/main/java/com/meloda/netservices/io/BytesOutputStream.kt +++ b/app/src/main/kotlin/com/meloda/fast/io/BytesOutputStream.kt @@ -1,4 +1,4 @@ -package com.meloda.netservices.io +package com.meloda.fast.io import java.io.ByteArrayOutputStream diff --git a/netservices/src/main/java/com/meloda/netservices/io/Charsets.kt b/app/src/main/kotlin/com/meloda/fast/io/Charsets.kt similarity index 85% rename from netservices/src/main/java/com/meloda/netservices/io/Charsets.kt rename to app/src/main/kotlin/com/meloda/fast/io/Charsets.kt index 90f91197..d9de4363 100644 --- a/netservices/src/main/java/com/meloda/netservices/io/Charsets.kt +++ b/app/src/main/kotlin/com/meloda/fast/io/Charsets.kt @@ -1,4 +1,4 @@ -package com.meloda.netservices.io +package com.meloda.fast.io import java.nio.charset.Charset import java.nio.charset.StandardCharsets diff --git a/netservices/src/main/java/com/meloda/netservices/io/EasyStreams.kt b/app/src/main/kotlin/com/meloda/fast/io/EasyStreams.kt similarity index 99% rename from netservices/src/main/java/com/meloda/netservices/io/EasyStreams.kt rename to app/src/main/kotlin/com/meloda/fast/io/EasyStreams.kt index 15c580af..af371aa5 100644 --- a/netservices/src/main/java/com/meloda/netservices/io/EasyStreams.kt +++ b/app/src/main/kotlin/com/meloda/fast/io/EasyStreams.kt @@ -1,4 +1,4 @@ -package com.meloda.netservices.io +package com.meloda.fast.io import org.jetbrains.annotations.Contract import java.io.* diff --git a/netservices/src/main/java/com/meloda/netservices/io/FileStreams.kt b/app/src/main/kotlin/com/meloda/fast/io/FileStreams.kt similarity index 98% rename from netservices/src/main/java/com/meloda/netservices/io/FileStreams.kt rename to app/src/main/kotlin/com/meloda/fast/io/FileStreams.kt index 40072705..f02f7e7d 100644 --- a/netservices/src/main/java/com/meloda/netservices/io/FileStreams.kt +++ b/app/src/main/kotlin/com/meloda/fast/io/FileStreams.kt @@ -1,4 +1,4 @@ -package com.meloda.netservices.io +package com.meloda.fast.io import org.jetbrains.annotations.Contract import java.io.* diff --git a/app/src/main/java/com/meloda/fast/model/NewUpdateInfo.kt b/app/src/main/kotlin/com/meloda/fast/model/NewUpdateInfo.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/model/NewUpdateInfo.kt rename to app/src/main/kotlin/com/meloda/fast/model/NewUpdateInfo.kt diff --git a/app/src/main/java/com/meloda/fast/model/UpdateInfo.kt b/app/src/main/kotlin/com/meloda/fast/model/UpdateInfo.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/model/UpdateInfo.kt rename to app/src/main/kotlin/com/meloda/fast/model/UpdateInfo.kt diff --git a/netservices/src/main/java/com/meloda/netservices/HttpRequest.kt b/app/src/main/kotlin/com/meloda/fast/net/HttpRequest.kt similarity index 92% rename from netservices/src/main/java/com/meloda/netservices/HttpRequest.kt rename to app/src/main/kotlin/com/meloda/fast/net/HttpRequest.kt index 2843c33e..a43dc092 100644 --- a/netservices/src/main/java/com/meloda/netservices/HttpRequest.kt +++ b/app/src/main/kotlin/com/meloda/fast/net/HttpRequest.kt @@ -1,7 +1,6 @@ -package com.meloda.netservices +package com.meloda.fast.net import androidx.collection.ArrayMap -import com.meloda.netservices.io.EasyStreams import java.io.IOException import java.io.InputStream import java.io.UnsupportedEncodingException @@ -37,7 +36,7 @@ class HttpRequest( fun asString(): String { val input = getStream() - val content = EasyStreams.read(input) + val content = com.meloda.fast.io.EasyStreams.read(input) connection?.disconnect() @@ -47,7 +46,7 @@ class HttpRequest( @Throws(IOException::class) fun asBytes(): ByteArray { val input = getStream() - val content = EasyStreams.readBytes(input) + val content = com.meloda.fast.io.EasyStreams.readBytes(input) connection?.disconnect() @@ -63,7 +62,7 @@ class HttpRequest( var input = connection!!.inputStream val encoding = connection!!.getHeaderField("Content-Encoding") if ("gzip".equals(encoding, ignoreCase = true)) { - input = EasyStreams.gzip(input) + input = com.meloda.fast.io.EasyStreams.gzip(input) } return input diff --git a/app/src/main/java/com/meloda/fast/receiver/DownloadUpdateReceiver.kt b/app/src/main/kotlin/com/meloda/fast/receiver/DownloadUpdateReceiver.kt similarity index 88% rename from app/src/main/java/com/meloda/fast/receiver/DownloadUpdateReceiver.kt rename to app/src/main/kotlin/com/meloda/fast/receiver/DownloadUpdateReceiver.kt index f23a5530..a375da0a 100644 --- a/app/src/main/java/com/meloda/fast/receiver/DownloadUpdateReceiver.kt +++ b/app/src/main/kotlin/com/meloda/fast/receiver/DownloadUpdateReceiver.kt @@ -3,7 +3,7 @@ package com.meloda.fast.receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import com.meloda.vksdk.OnResponseListener +import com.meloda.fast.api.OnResponseListener open class DownloadUpdateReceiver : BroadcastReceiver() { diff --git a/app/src/main/java/com/meloda/fast/receiver/MinuteReceiver.kt b/app/src/main/kotlin/com/meloda/fast/receiver/MinuteReceiver.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/receiver/MinuteReceiver.kt rename to app/src/main/kotlin/com/meloda/fast/receiver/MinuteReceiver.kt diff --git a/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt new file mode 100644 index 00000000..77333572 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/friends/FriendsFragment.kt @@ -0,0 +1,21 @@ +package com.meloda.fast.screens.friends + +import android.os.Bundle +import android.view.View +import android.viewbinding.library.fragment.viewBinding +import com.meloda.fast.R +import com.meloda.fast.base.BaseFragment +import com.meloda.fast.databinding.FragmentFriendsBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class FriendsFragment : BaseFragment(R.layout.fragment_friends) { + + private val binding: FragmentFriendsBinding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt new file mode 100644 index 00000000..e52e4e70 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/important/ImportantFragment.kt @@ -0,0 +1,21 @@ +package com.meloda.fast.screens.important + +import android.os.Bundle +import android.view.View +import android.viewbinding.library.fragment.viewBinding +import com.meloda.fast.R +import com.meloda.fast.base.BaseFragment +import com.meloda.fast.databinding.FragmentImportantBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class ImportantFragment : BaseFragment(R.layout.fragment_important) { + + private val binding: FragmentImportantBinding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt new file mode 100644 index 00000000..f9577eba --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginFragment.kt @@ -0,0 +1,290 @@ +package com.meloda.fast.screens.login + +import android.annotation.SuppressLint +import android.graphics.Typeface +import android.os.Bundle +import android.view.KeyEvent +import android.view.View +import android.view.inputmethod.EditorInfo +import android.viewbinding.library.fragment.viewBinding +import android.webkit.CookieManager +import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf +import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.setFragmentResultListener +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import coil.load +import coil.transform.RoundedCornersTransformation +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputLayout +import com.meloda.fast.R +import com.meloda.fast.base.BaseVMFragment +import com.meloda.fast.base.viewmodel.StartProgressEvent +import com.meloda.fast.base.viewmodel.StopProgressEvent +import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.databinding.DialogCaptchaBinding +import com.meloda.fast.databinding.FragmentLoginBinding +import com.meloda.fast.screens.main.MainFragment +import com.meloda.fast.util.KeyboardUtils +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.util.* +import kotlin.concurrent.schedule + +@AndroidEntryPoint +class LoginFragment : BaseVMFragment(R.layout.fragment_login) { + + override val viewModel: LoginVM by viewModels() + private val binding: FragmentLoginBinding by viewBinding() + + private var lastEmail: String = "" + private var lastPassword: String = "" + + private var errorTimer: Timer? = null + private var captchaInputLayout: TextInputLayout? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (parentFragment?.parentFragment as? MainFragment)?.bottomBar?.isVisible = false + + prepareViews() + + binding.loginInput.clearFocus() + + setFragmentResultListener("validation") { _, bundle -> + lifecycleScope.launch { viewModel.getValidatedData(bundle) } + } + +// showCaptchaDialog( +// "https://www.vets4pets.com/syssiteassets/species/cat/kitten/tiny-kitten-in-field.jpg?width=1040", +// "" +// ) + } + + override fun onEvent(event: VKEvent) { + super.onEvent(event) + + when (event) { + is ShowError -> showErrorSnackbar(event.errorDescription) + is ShowCaptchaDialog -> showCaptchaDialog(event.captchaImage, event.captchaSid) + is GoToValidationEvent -> goToValidation(event.redirectUrl) + is GoToMainEvent -> goToMain(event.haveAuthorized) + StartProgressEvent -> onProgressStarted() + StopProgressEvent -> onProgressStopped() + } + } + + private fun onProgressStarted() { + binding.loginContainer.isVisible = false + binding.passwordContainer.isVisible = false + binding.auth.isVisible = false + binding.progress.isVisible = true + } + + private fun onProgressStopped() { + binding.loginContainer.isVisible = true + binding.passwordContainer.isVisible = true + binding.auth.isVisible = true + binding.progress.isVisible = false + } + + private fun prepareViews() { + prepareWebView() + prepareEmailEditText() + preparePasswordEditText() + prepareAuthButton() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun prepareWebView() { + with(binding.webView) { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.loadsImagesAutomatically = false + settings.userAgentString = "Chrome/41.0.2228.0 Safari/537.36" + clearCache(true) + } + + val cookieManager = CookieManager.getInstance() + cookieManager.removeAllCookies(null) + cookieManager.flush() + cookieManager.setAcceptCookie(false) + } + + private fun prepareEmailEditText() { + binding.loginInput.addTextChangedListener { + if (!binding.loginLayout.error.isNullOrBlank()) binding.loginLayout.error = "" + } + } + + private fun preparePasswordEditText() { + binding.passwordInput.typeface = Typeface.DEFAULT + binding.passwordLayout.endIconMode = TextInputLayout.END_ICON_NONE + + binding.passwordInput.addTextChangedListener { + if (!binding.passwordLayout.error.isNullOrBlank()) binding.passwordLayout.error = "" + } + + binding.passwordInput.setOnFocusChangeListener { _, hasFocus -> + binding.passwordLayout.endIconMode = + if (hasFocus) TextInputLayout.END_ICON_PASSWORD_TOGGLE + else TextInputLayout.END_ICON_NONE + } + + binding.passwordInput.setOnEditorActionListener { _, _, event -> + if (event == null) return@setOnEditorActionListener false + return@setOnEditorActionListener if (event.action == EditorInfo.IME_ACTION_GO || + (event.action == KeyEvent.ACTION_DOWN && (event.keyCode == KeyEvent.KEYCODE_ENTER || event.keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER)) + ) { + KeyboardUtils.hideKeyboardFrom(binding.passwordInput) + binding.auth.performClick() + true + } else false + } + } + + private fun prepareAuthButton() { + binding.auth.setOnClickListener { + if (binding.progress.isVisible) return@setOnClickListener + + val loginString = binding.loginInput.text.toString().trim() + val passwordString = binding.passwordInput.text.toString().trim() + + if (!validateInputData(loginString, passwordString)) return@setOnClickListener + + KeyboardUtils.hideKeyboardFrom(requireView().findFocus()) + + lifecycleScope.launch { + viewModel.login( + binding.webView, + loginString, + passwordString + ) + } + } + } + + // TODO: 7/27/2021 extract strings to resources + private fun validateInputData( + loginString: String?, + passwordString: String?, + captchaCode: String? = null + ): Boolean { + var isValidated = true + + if (loginString?.isEmpty() == true) { + isValidated = false + setError("Input login", binding.loginLayout) + } + + if (passwordString?.isEmpty() == true) { + isValidated = false + setError("Input password", binding.passwordLayout) + } + + if (captchaCode?.isEmpty() == true && captchaInputLayout != null) { + isValidated = false + setError("Input code", captchaInputLayout!!) + } + + return isValidated + } + + private fun setError(error: String, inputLayout: TextInputLayout) { + inputLayout.error = error + + if (errorTimer != null) { + errorTimer?.cancel() + errorTimer = null + } + + if (errorTimer == null) { + errorTimer = Timer() + } + + errorTimer?.schedule(2500) { + lifecycleScope.launch(Dispatchers.Main) { clearErrors() } + } + } + + private fun clearErrors() { + binding.loginLayout.error = "" + binding.passwordLayout.error = "" + + captchaInputLayout?.error = "" + } + + private fun showCaptchaDialog(captchaImage: String, captchaSid: String) { + val captchaBinding = DialogCaptchaBinding.inflate(layoutInflater, null, false) + captchaInputLayout = captchaBinding.captchaLayout + + captchaBinding.image.load(captchaImage) { + crossfade(100) + transformations(RoundedCornersTransformation(4f)) + } + + val builder = AlertDialog.Builder(requireContext()) + .setView(captchaBinding.root) + .setCancelable(false) + .setTitle(R.string.input_captcha) + + val dialog = builder.show() + + captchaBinding.ok.setOnClickListener { + val captchaCode = captchaBinding.captchaInput.text.toString().trim() + + if (!validateInputData( + loginString = null, + passwordString = null, + captchaCode = captchaCode + ) + ) return@setOnClickListener + + dialog.dismiss() + + lifecycleScope.launch { + viewModel.login( + webView = binding.webView, + email = lastEmail, + password = lastPassword, + captchaSid = captchaSid, + captchaKey = captchaCode + ) + } + } + captchaBinding.cancel.setOnClickListener { dialog.dismiss() } + } + + private fun showErrorSnackbar(errorDescription: String) { + val snackbar = Snackbar.make( + requireView(), + getString(R.string.error, errorDescription), + Snackbar.LENGTH_LONG + ) + + snackbar.animationMode = Snackbar.ANIMATION_MODE_FADE + snackbar.show() + } + + private fun goToValidation(redirectUrl: String) { + findNavController().navigate( + R.id.toValidation, + bundleOf("redirectUrl" to redirectUrl) + ) + } + + private fun goToMain(haveAuthorized: Boolean) { + lifecycleScope.launch { + if (haveAuthorized) delay(500) + + findNavController().navigate(R.id.toMain) + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/LoginVM.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginVM.kt new file mode 100644 index 00000000..f25ed0d9 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/login/LoginVM.kt @@ -0,0 +1,127 @@ +package com.meloda.fast.screens.login + +import android.os.Bundle +import android.util.Log +import android.webkit.JavascriptInterface +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.lifecycle.viewModelScope +import com.meloda.fast.UserConfig +import com.meloda.fast.api.VKAuth +import com.meloda.fast.base.viewmodel.BaseVM +import com.meloda.fast.base.viewmodel.StartProgressEvent +import com.meloda.fast.base.viewmodel.StopProgressEvent +import com.meloda.fast.base.viewmodel.VKEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.json.JSONObject +import org.jsoup.Jsoup + +class LoginVM : BaseVM() { + + private var isWebViewPrepared = false + + suspend fun login( + webView: WebView, + email: String, + password: String, + captchaSid: String? = null, + captchaKey: String? = null + ) { + sendEvent(StartProgressEvent) + + val urlToGo = VKAuth.getDirectAuthUrl(email, password, captchaSid, captchaKey) + + if (!isWebViewPrepared) { + isWebViewPrepared = true + + webView.addJavascriptInterface(WebViewHandlerInterface(), "HtmlHandler") + + webView.webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + webView.loadUrl( + "javascript:window.HtmlHandler.handleHtml" + + "(''+document.getElementsByTagName('html')[0].innerHTML+'');" + ) + } + } + } + + webView.loadUrl(urlToGo) + } + + @Suppress("MoveVariableDeclarationIntoWhen") + private fun checkResponse(response: JSONObject) { + viewModelScope.launch(Dispatchers.Default) { + if (response.has("error")) { + sendEvent(StopProgressEvent) + + val errorString = response.optString("error") + val errorDescription = response.optString("error_description") + + // TODO: 7/27/2021 use this with localized resources +// val errorType = response.optString("error_type") + + when (errorString) { + "need_validation" -> { + val redirectUrl = response.optString("redirect_uri") + + tasksEventChannel.send(GoToValidationEvent(redirectUrl)) + } + "need_captcha" -> { + val captchaImage = response.optString("captcha_img") + val captchaSid = response.optString("captcha_sid") + + Log.d("CAPTCHA", "captchaImage: $captchaImage") + + tasksEventChannel.send(ShowCaptchaDialog(captchaImage, captchaSid)) + } + else -> { + tasksEventChannel.send(ShowError(errorDescription)) + } + } + } else { + delay(1500) + sendEvent(StopProgressEvent) + + val userId = response.optInt("user_id", -1) + val accessToken = response.optString("access_token") + + UserConfig.accessToken = accessToken + UserConfig.userId = userId + + tasksEventChannel.send(GoToMainEvent()) + } + } + } + + suspend fun getValidatedData(bundle: Bundle) { + val accessToken = bundle.getString("token") ?: "" + val userId = bundle.getInt("userId") + + UserConfig.accessToken = accessToken + UserConfig.userId = userId + + tasksEventChannel.send(GoToMainEvent()) + } + + inner class WebViewHandlerInterface { + @JavascriptInterface + fun handleHtml(html: String) { + val doc = Jsoup.parse(html) + + val responseString = + doc.select("pre[style=\"word-wrap: break-word; white-space: pre-wrap;\"]").first() + ?.text() ?: "" + + checkResponse(JSONObject(responseString)) + } + } + +} + +data class ShowError(val errorDescription: String) : VKEvent() +data class ShowCaptchaDialog(val captchaImage: String, val captchaSid: String) : VKEvent() +data class GoToValidationEvent(val redirectUrl: String) : VKEvent() +data class GoToMainEvent(val haveAuthorized: Boolean = true) : VKEvent() \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/login/ValidationFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/login/ValidationFragment.kt new file mode 100644 index 00000000..62db202e --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/login/ValidationFragment.kt @@ -0,0 +1,75 @@ +package com.meloda.fast.screens.login + +import android.graphics.Bitmap +import android.os.Bundle +import android.util.Log +import android.view.View +import android.view.ViewGroup +import android.viewbinding.library.fragment.viewBinding +import android.webkit.CookieManager +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.core.os.bundleOf +import androidx.navigation.fragment.findNavController +import com.meloda.fast.R +import com.meloda.fast.api.VKAuth +import com.meloda.fast.base.BaseFragment +import com.meloda.fast.databinding.FragmentValidationBinding + +class ValidationFragment : BaseFragment(R.layout.fragment_validation) { + + private val binding: FragmentValidationBinding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val redirectUrl = getRedirectUrl() + + binding.webView.webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + Log.d("Fast::Validation", "onPageStarted: url: $url") + parseUrl(url ?: "") + } + } + + binding.webView.settings.domStorageEnabled = true + binding.webView.clearCache(true) + binding.webView.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + + val manager = CookieManager.getInstance() + manager.removeAllCookies(null) + manager.flush() + manager.setAcceptCookie(true) + + binding.webView.loadUrl(redirectUrl) + } + + private fun getRedirectUrl() = requireArguments().getString("redirectUrl", "") + + private fun parseUrl(url: String) { + if (url.startsWith("https://oauth.vk.com/blank.html#success=1")) { + if (!url.contains("error=")) { + val data = VKAuth.parseRedirectUrl(url) + + val accessToken = data.first + val userId = data.second + + parentFragmentManager.setFragmentResult( + "validation", + bundleOf( + "accessToken" to accessToken, + "userId" to userId + ) + ) + + findNavController().navigate(R.id.toLogin) + } + } else { + Log.d("Fast::Validation", "parseUrl: $url") + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt new file mode 100644 index 00000000..7a697e2c --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/main/MainFragment.kt @@ -0,0 +1,51 @@ +package com.meloda.fast.screens.main + +import android.os.Bundle +import android.view.View +import android.viewbinding.library.fragment.viewBinding +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.meloda.fast.R +import com.meloda.fast.UserConfig +import com.meloda.fast.base.BaseVMFragment +import com.meloda.fast.databinding.FragmentMainBinding +import com.meloda.fast.extensions.NavigationExtensions.setupWithNavController +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainFragment : BaseVMFragment(R.layout.fragment_main) { + + override val viewModel: MainVM by viewModels() + private val binding: FragmentMainBinding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (savedInstanceState == null) setupBottomBar() + + if (!UserConfig.isLoggedIn()) findNavController().navigate(R.id.toLogin) + } + + private fun setupBottomBar() { + val navGraphIds = listOf( + R.navigation.messages, + R.navigation.friends, + R.navigation.important, + R.navigation.login + ) + + with(binding.bottomBar) { + selectedItemId = R.id.messages + setupWithNavController( + navGraphIds = navGraphIds, + fragmentManager = childFragmentManager, + containerId = R.id.fragmentContainer, + intent = requireActivity().intent + ) + } + } + + val bottomBar get() = binding.bottomBar + + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/main/MainVM.kt b/app/src/main/kotlin/com/meloda/fast/screens/main/MainVM.kt new file mode 100644 index 00000000..39b40f31 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/main/MainVM.kt @@ -0,0 +1,5 @@ +package com.meloda.fast.screens.main + +import com.meloda.fast.base.viewmodel.BaseVM + +class MainVM : BaseVM() \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt new file mode 100644 index 00000000..4c2a978a --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsAdapter.kt @@ -0,0 +1,41 @@ +package com.meloda.fast.screens.messages + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import com.meloda.fast.api.model.VKConversation +import com.meloda.fast.base.adapter.BaseAdapter +import com.meloda.fast.base.adapter.BindingHolder +import com.meloda.fast.databinding.ItemConversationBinding + +class ConversationsAdapter(context: Context, values: ArrayList) : + BaseAdapter( + context, values, COMPARATOR + ) { + + companion object { + private val COMPARATOR = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: VKConversation, + newItem: VKConversation + ) = false + + override fun areContentsTheSame( + oldItem: VKConversation, + newItem: VKConversation + ) = false + } + } + + inner class ItemHolder(binding: ItemConversationBinding) : + BindingHolder(binding) { + + override fun bind(position: Int) { + binding.title.text = getItem(position).title ?: "HUI" + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + ItemHolder(ItemConversationBinding.inflate(inflater, parent, false)) + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt new file mode 100644 index 00000000..523359c3 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsFragment.kt @@ -0,0 +1,83 @@ +package com.meloda.fast.screens.messages + +import android.os.Bundle +import android.view.View +import android.viewbinding.library.fragment.viewBinding +import androidx.core.view.isVisible +import androidx.fragment.app.viewModels +import com.meloda.fast.R +import com.meloda.fast.base.BaseVMFragment +import com.meloda.fast.base.viewmodel.StartProgressEvent +import com.meloda.fast.base.viewmodel.StopProgressEvent +import com.meloda.fast.base.viewmodel.VKEvent +import com.meloda.fast.databinding.FragmentConversationsBinding +import com.meloda.fast.util.AndroidUtils +import dagger.hilt.android.AndroidEntryPoint +import kotlin.math.roundToInt + +@AndroidEntryPoint +class ConversationsFragment : BaseVMFragment(R.layout.fragment_conversations) { + + override val viewModel: ConversationsVM by viewModels() + private val binding: FragmentConversationsBinding by viewBinding() + + private lateinit var adapter: ConversationsAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + prepareViews() + + viewModel.loadConversations() + } + + override fun onEvent(event: VKEvent) { + super.onEvent(event) + when (event) { + StartProgressEvent -> onProgressStarted() + StopProgressEvent -> onProgressStopped() + } + } + + private fun onProgressStarted() { + if (adapter.isEmpty()) + binding.progressBar.isVisible = true + } + + private fun onProgressStopped() { + binding.progressBar.isVisible = false + } + + private fun prepareViews() { + prepareRecyclerView() + prepareRefreshLayout() + } + + private fun prepareRecyclerView() { + + } + + private fun prepareRefreshLayout() { + with(binding.refreshLayout) { + setProgressViewOffset( + true, + AndroidUtils.px(40).roundToInt(), + AndroidUtils.px(96).roundToInt() + ) + setProgressBackgroundColorSchemeColor( + AndroidUtils.getThemeAttrColor( + requireContext(), + R.attr.colorSurface + ) + ) + setColorSchemeColors( + AndroidUtils.getThemeAttrColor( + requireContext(), + R.attr.colorAccent + ) + ) + setOnRefreshListener { } + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsVM.kt b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsVM.kt new file mode 100644 index 00000000..e9479a86 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/screens/messages/ConversationsVM.kt @@ -0,0 +1,13 @@ +package com.meloda.fast.screens.messages + +import androidx.lifecycle.viewModelScope +import com.meloda.fast.base.viewmodel.BaseVM +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class ConversationsVM : BaseVM() { + + fun loadConversations() = viewModelScope.launch(Dispatchers.Default) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/service/LongPollService.kt b/app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt similarity index 95% rename from app/src/main/java/com/meloda/fast/service/LongPollService.kt rename to app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt index 32ce8a2e..f2ad56da 100644 --- a/app/src/main/java/com/meloda/fast/service/LongPollService.kt +++ b/app/src/main/kotlin/com/meloda/fast/service/LongPollService.kt @@ -6,13 +6,13 @@ import android.os.IBinder import android.util.Log import androidx.annotation.WorkerThread import androidx.collection.arrayMapOf -import com.meloda.concurrent.LowThread import com.meloda.fast.UserConfig import com.meloda.fast.VKLongPollParser +import com.meloda.fast.api.VKApi +import com.meloda.fast.api.model.VKLongPollServer +import com.meloda.fast.concurrent.LowThread +import com.meloda.fast.net.HttpRequest import com.meloda.fast.util.AndroidUtils -import com.meloda.netservices.HttpRequest -import com.meloda.vksdk.VKApi -import com.meloda.vksdk.model.VKLongPollServer import org.json.JSONArray import org.json.JSONObject diff --git a/app/src/main/java/com/meloda/fast/util/AndroidUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt similarity index 78% rename from app/src/main/java/com/meloda/fast/util/AndroidUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt index 2f0a75d4..ce6197a9 100644 --- a/app/src/main/java/com/meloda/fast/util/AndroidUtils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/AndroidUtils.kt @@ -1,10 +1,8 @@ package com.meloda.fast.util -import android.content.ClipData import android.content.Context import android.content.Intent import android.content.res.Configuration -import android.net.NetworkCapabilities import android.net.Uri import android.os.Build import android.provider.Settings @@ -38,18 +36,20 @@ object AndroidUtils { } } + //TODO fun hasConnection(): Boolean { - val network = AppGlobal.connectivityManager.activeNetwork ?: return false - val activeNetwork = - AppGlobal.connectivityManager.getNetworkCapabilities(network) ?: return false - - return when { - activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true - activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true - activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true - activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true - else -> false - } + return false +// val network = AppGlobal.connectivityManager.activeNetwork ?: return false +// val activeNetwork = +// AppGlobal.connectivityManager.getNetworkCapabilities(network) ?: return false +// +// return when { +// activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true +// activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true +// activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true +// activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true +// else -> false +// } } fun getDisplayWidth(): Int { @@ -88,8 +88,9 @@ object AndroidUtils { }) } + //TODO fun copyText(label: String? = "", text: String) { - AppGlobal.clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text)) +// AppGlobal.clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text)) } fun getThemeAttrColor(context: Context, @AttrRes resId: Int): Int { diff --git a/arrayutils/src/main/java/com/meloda/arrayutils/ArrayUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/ArrayUtils.kt similarity index 98% rename from arrayutils/src/main/java/com/meloda/arrayutils/ArrayUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/ArrayUtils.kt index 7bb66fee..de96a79d 100644 --- a/arrayutils/src/main/java/com/meloda/arrayutils/ArrayUtils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/ArrayUtils.kt @@ -1,4 +1,4 @@ -package com.meloda.arrayutils +package com.meloda.fast.util import java.util.stream.Collectors diff --git a/app/src/main/java/com/meloda/fast/util/ColorUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/ColorUtils.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/util/ColorUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/ColorUtils.kt diff --git a/app/src/main/kotlin/com/meloda/fast/util/ImageUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/ImageUtils.kt new file mode 100644 index 00000000..c1d2c056 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/util/ImageUtils.kt @@ -0,0 +1,55 @@ +package com.meloda.fast.util + +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView + +object ImageUtils { + + fun loadImage(image: String, imageView: ImageView, placeholder: Drawable?) { + if (image.isEmpty()) return + +// if (imageView is SimpleDraweeView) { +// imageView.setImageURI(image) +// return +// } +// +// val picasso = Picasso.get() +// .load(image) +// .priority(Picasso.Priority.LOW) + +// if (placeholder != null) picasso.placeholder(placeholder) +// +// picasso.into(imageView) + } + + fun loadImage(image: String?, listener: OnLoadListener?) { + if (image.isNullOrEmpty()) return + +// val picasso = Picasso.get() +// .load(image) +// .priority(Picasso.Priority.LOW) +// +// val target = object : Target { +// override fun onPrepareLoad(placeHolderDrawable: Drawable?) { +// +// } +// +// override fun onBitmapFailed(e: Exception, errorDrawable: Drawable?) { +// listener?.onError(e) +// } +// +// override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { +// listener?.onLoad(bitmap) +// } +// } + +// picasso.into(target) + } + + + interface OnLoadListener { + fun onLoad(bitmap: Bitmap) + fun onError(e: Exception) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/meloda/fast/util/KeyboardUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/KeyboardUtils.kt new file mode 100644 index 00000000..2c76a379 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/util/KeyboardUtils.kt @@ -0,0 +1,16 @@ +package com.meloda.fast.util + +import android.view.View +import com.meloda.fast.common.AppGlobal + +object KeyboardUtils { + + fun hideKeyboardFrom(focusedView: View?) { + AppGlobal.inputMethodManager.hideSoftInputFromWindow(focusedView?.windowToken, 0) + } + + fun showKeyboard(viewToFocus: View) { + AppGlobal.inputMethodManager.showSoftInput(viewToFocus, 0) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/util/TextUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/TextUtils.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/util/TextUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/TextUtils.kt diff --git a/app/src/main/java/com/meloda/fast/util/TimeUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/util/TimeUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/TimeUtils.kt diff --git a/app/src/main/java/com/meloda/fast/util/Utils.kt b/app/src/main/kotlin/com/meloda/fast/util/Utils.kt similarity index 91% rename from app/src/main/java/com/meloda/fast/util/Utils.kt rename to app/src/main/kotlin/com/meloda/fast/util/Utils.kt index 3e150a06..32497083 100644 --- a/app/src/main/java/com/meloda/fast/util/Utils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/Utils.kt @@ -1,9 +1,9 @@ package com.meloda.fast.util import android.content.Context -import com.meloda.arrayutils.ArrayUtils.isNullOrEmpty +import com.meloda.fast.util.ArrayUtils.isNullOrEmpty import com.meloda.fast.R -import com.meloda.netservices.io.BytesOutputStream +import com.meloda.fast.io.BytesOutputStream import java.io.ByteArrayInputStream import java.io.IOException import java.io.ObjectInputStream diff --git a/app/src/main/java/com/meloda/fast/util/VKUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt similarity index 94% rename from app/src/main/java/com/meloda/fast/util/VKUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt index ada15629..b97e060f 100644 --- a/app/src/main/java/com/meloda/fast/util/VKUtils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/VKUtils.kt @@ -4,17 +4,16 @@ import android.content.Context import android.graphics.drawable.Drawable import androidx.annotation.WorkerThread import androidx.core.content.ContextCompat -import com.amulyakhare.textdrawable.TextDrawable -import com.meloda.concurrent.TaskManager -import com.meloda.extensions.ContextExtensions.color -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.extensions.StringExtensions.lowerCase +import com.meloda.fast.concurrent.TaskManager +import com.meloda.fast.extensions.ContextExtensions.color +import com.meloda.fast.extensions.ContextExtensions.drawable +import com.meloda.fast.extensions.DrawableExtensions.tint +import com.meloda.fast.extensions.StringExtensions.lowerCase import com.meloda.fast.R +import com.meloda.fast.api.model.* import com.meloda.fast.common.AppGlobal -import com.meloda.vksdk.OnResponseListener -import com.meloda.vksdk.model.* -import com.meloda.vksdk.util.VKUtil +import com.meloda.fast.api.OnResponseListener +import com.meloda.fast.api.util.VKUtil import java.text.SimpleDateFormat import java.util.* import kotlin.math.abs @@ -73,14 +72,14 @@ object VKUtils { return getUserOnlineIcon(context, null, user) } - fun getAvatarPlaceholder(context: Context, dialogTitle: String): TextDrawable { - return TextDrawable.builder().buildRound( - if (dialogTitle.isEmpty()) "" else { - TextUtils.getFirstLetterFromString(dialogTitle) - }, - context.color(R.color.accent) - ) - } + // fun getAvatarPlaceholder(context: Context, dialogTitle: String): TextDrawable { +// return TextDrawable.builder().buildRound( +// if (dialogTitle.isEmpty()) "" else { +// TextUtils.getFirstLetterFromString(dialogTitle) +// }, +// context.color(R.color.accent) +// ) +// } @Deprecated("") @WorkerThread diff --git a/app/src/main/java/com/meloda/fast/util/ViewUtils.kt b/app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt similarity index 85% rename from app/src/main/java/com/meloda/fast/util/ViewUtils.kt rename to app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt index 7c1c6516..68a91afd 100644 --- a/app/src/main/java/com/meloda/fast/util/ViewUtils.kt +++ b/app/src/main/kotlin/com/meloda/fast/util/ViewUtils.kt @@ -7,12 +7,11 @@ import android.view.View import android.widget.TextView import android.widget.Toast import com.google.android.material.snackbar.Snackbar -import com.meloda.extensions.ContextExtensions.color +import com.meloda.fast.extensions.ContextExtensions.color import com.meloda.fast.R import com.meloda.fast.widget.CircleImageView -import com.meloda.vksdk.model.VKUser -import com.meloda.vksdk.util.VKUtil -import com.squareup.picasso.Picasso +import com.meloda.fast.api.model.VKUser +import com.meloda.fast.api.util.VKUtil object ViewUtils { @@ -47,7 +46,7 @@ object ViewUtils { val profileAvatar: CircleImageView = view.findViewById(R.id.headerAvatar) if (AndroidUtils.hasConnection()) { - Picasso.get().load(VKUtil.getUserPhoto(user)).into(profileAvatar) +// Picasso.get().load(VKUtil.getUserPhoto(user)).into(profileAvatar) } else { profileAvatar.setImageDrawable(ColorDrawable(view.context.color(R.color.accent))) } diff --git a/app/src/main/kotlin/com/meloda/fast/viewmodel/ChatsViewModel.kt b/app/src/main/kotlin/com/meloda/fast/viewmodel/ChatsViewModel.kt new file mode 100644 index 00000000..02d56102 --- /dev/null +++ b/app/src/main/kotlin/com/meloda/fast/viewmodel/ChatsViewModel.kt @@ -0,0 +1,70 @@ +package com.meloda.fast.viewmodel + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.meloda.fast.database.CacheStorage +import com.meloda.fast.api.VKApi +import com.meloda.fast.api.VKConstants +import com.meloda.fast.api.model.VKConversation +import com.meloda.fast.api.model.VKMessage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class ChatsViewModel : ViewModel() { + + val chatsAnswer = MutableLiveData() + + data class ChatsAnswer(var status: Status, var message: String? = "") { + companion object { + val SUCCESS get() = ChatsAnswer(Status.SUCCESS) + val FAIL get() = ChatsAnswer(Status.FAIL) + val LOADING get() = ChatsAnswer(Status.LOADING) + } + + enum class Status { + SUCCESS, FAIL, LOADING + } + } + + fun loadChats() = viewModelScope.launch(Dispatchers.IO) { + chatsAnswer.postValue(ChatsAnswer.LOADING) + + try { + val chats = VKApi.messages() + .getConversations() + .filter("all") + .extended(true) + .fields(VKConstants.USER_FIELDS + ',' + VKConstants.GROUP_FIELDS) + .offset(0) + .count(30) + .executeSuspend(VKConversation::class.java) + +// CacheStorage.chatsStorage.insertValues(chats) + + val lastMessages = arrayListOf() + chats.collect { + lastMessages.add(it.lastMessage) + } + + CacheStorage.messagesStorage.insertValues(lastMessages) + CacheStorage.usersStorage.insertValues(VKConversation.profiles) + CacheStorage.groupsStorage.insertValues(VKConversation.groups) +// +// chatsAnswer.value = ChatsAnswer.SUCCESS + + chatsAnswer.postValue(ChatsAnswer.SUCCESS) + + withContext(Dispatchers.Main) { +// adapter.updateValues(chats.toList()) +// adapter.notifyDataSetChanged() + } + } catch (e: Exception) { + chatsAnswer.postValue(ChatsAnswer.FAIL.also { it.message = e.message }) +// chatsAnswer.value = ChatsAnswer.FAIL.also { it.message = e.message } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/widget/BoundedLinearLayout.kt b/app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/widget/BoundedLinearLayout.kt rename to app/src/main/kotlin/com/meloda/fast/widget/BoundedLinearLayout.kt diff --git a/app/src/main/java/com/meloda/fast/widget/CircleImageView.kt b/app/src/main/kotlin/com/meloda/fast/widget/CircleImageView.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/widget/CircleImageView.kt rename to app/src/main/kotlin/com/meloda/fast/widget/CircleImageView.kt diff --git a/app/src/main/java/com/meloda/fast/widget/NoItemsView.kt b/app/src/main/kotlin/com/meloda/fast/widget/NoItemsView.kt similarity index 87% rename from app/src/main/java/com/meloda/fast/widget/NoItemsView.kt rename to app/src/main/kotlin/com/meloda/fast/widget/NoItemsView.kt index 0f7b6adb..5c1c9729 100644 --- a/app/src/main/java/com/meloda/fast/widget/NoItemsView.kt +++ b/app/src/main/kotlin/com/meloda/fast/widget/NoItemsView.kt @@ -11,11 +11,10 @@ import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.view.isVisible -import com.meloda.extensions.ContextExtensions.drawable -import com.meloda.extensions.DrawableExtensions.tint -import com.meloda.extensions.FloatExtensions.int +import com.meloda.fast.extensions.ContextExtensions.drawable +import com.meloda.fast.extensions.DrawableExtensions.tint +import com.meloda.fast.extensions.FloatExtensions.int import com.meloda.fast.R -import com.meloda.fast.base.BaseAdapter import com.meloda.fast.util.AndroidUtils @Suppress("UNCHECKED_CAST") @@ -26,8 +25,6 @@ class NoItemsView @JvmOverloads constructor( private lateinit var noItemsPicture: ImageView private lateinit var noItemsTextView: TextView - private var recyclerView: RecyclerView? = null - private val textViewParams get() = LayoutParams( LayoutParams.MATCH_PARENT, @@ -129,16 +126,6 @@ class NoItemsView @JvmOverloads constructor( noItemsTextView.setTextColor(color) } - fun syncWith(recyclerView: RecyclerView) { - this.recyclerView = recyclerView - - recyclerView.noItemsView = this - } - - fun > updateState(adapter: ADAPTER) { - if (adapter.isEmpty()) show() else hide() - } - fun show() { isVisible = true } diff --git a/app/src/main/java/com/meloda/fast/widget/Toolbar.kt b/app/src/main/kotlin/com/meloda/fast/widget/Toolbar.kt similarity index 95% rename from app/src/main/java/com/meloda/fast/widget/Toolbar.kt rename to app/src/main/kotlin/com/meloda/fast/widget/Toolbar.kt index e792312d..15b24544 100644 --- a/app/src/main/java/com/meloda/fast/widget/Toolbar.kt +++ b/app/src/main/kotlin/com/meloda/fast/widget/Toolbar.kt @@ -15,7 +15,6 @@ import android.widget.TextView import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.core.content.res.ResourcesCompat -import com.facebook.drawee.view.SimpleDraweeView import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.theme.overlay.MaterialThemeOverlay import com.meloda.fast.R @@ -97,7 +96,7 @@ class Toolbar : MaterialToolbar { } fun setAvatarIcon(@DrawableRes resId: Int) { - findViewById(R.id.toolbarAvatar).setActualImageResource(resId) +// findViewById(R.id.toolbarAvatar).setActualImageResource(resId) } fun setAvatarClickListener(listener: OnClickListener?) { @@ -108,9 +107,9 @@ class Toolbar : MaterialToolbar { findViewById(R.id.toolbarAvatar).visibility = if (visible) VISIBLE else GONE } - fun getAvatar(): SimpleDraweeView { - return findViewById(R.id.toolbarAvatar) - } +// fun getAvatar(): SimpleDraweeView { +// return findViewById(R.id.toolbarAvatar) +// } fun setTitleMode(titleMode: TitleMode) { val title = findViewById(R.id.toolbarTitle) diff --git a/app/src/main/java/com/meloda/fast/widget/WrapTextView.kt b/app/src/main/kotlin/com/meloda/fast/widget/WrapTextView.kt similarity index 100% rename from app/src/main/java/com/meloda/fast/widget/WrapTextView.kt rename to app/src/main/kotlin/com/meloda/fast/widget/WrapTextView.kt diff --git a/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml b/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml new file mode 100644 index 00000000..1fc37dc3 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ic_security.xml b/app/src/main/res/drawable-v21/ic_security.xml new file mode 100644 index 00000000..ef2e5521 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_security.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/edit_text_box_background.xml b/app/src/main/res/drawable/edit_text_box_background.xml new file mode 100644 index 00000000..d70c9f09 --- /dev/null +++ b/app/src/main/res/drawable/edit_text_box_background.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/toolbar_background.xml b/app/src/main/res/drawable/toolbar_background.xml index ede352aa..adb66b4b 100644 --- a/app/src/main/res/drawable/toolbar_background.xml +++ b/app/src/main/res/drawable/toolbar_background.xml @@ -4,7 +4,7 @@ - + - + xmlns:tools="http://schemas.android.com/tools"> - - - - - - - - - + android:orientation="vertical" + tools:context=".MainActivity"> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_update.xml b/app/src/main/res/layout/activity_update.xml index dfdca701..338b9f50 100644 --- a/app/src/main/res/layout/activity_update.xml +++ b/app/src/main/res/layout/activity_update.xml @@ -63,13 +63,13 @@ android:gravity="center" android:visibility="gone"> - + + + + + + + diff --git a/app/src/main/res/layout/dialog_captcha.xml b/app/src/main/res/layout/dialog_captcha.xml new file mode 100644 index 00000000..cbdb0817 --- /dev/null +++ b/app/src/main/res/layout/dialog_captcha.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_conversations.xml b/app/src/main/res/layout/fragment_conversations.xml index 303d2f4d..4c3f8723 100644 --- a/app/src/main/res/layout/fragment_conversations.xml +++ b/app/src/main/res/layout/fragment_conversations.xml @@ -1,36 +1,47 @@ - + + + + + + - - - - - - - - - + android:background="@drawable/toolbar_background" + android:elevation="3dp" + app:title="@string/conversations" + app:titleCentered="true" /> + android:visibility="gone" + tools:visibility="visible" /> + + diff --git a/app/src/main/res/layout/fragment_important.xml b/app/src/main/res/layout/fragment_important.xml index 7fd71ed8..d5ad0418 100644 --- a/app/src/main/res/layout/fragment_important.xml +++ b/app/src/main/res/layout/fragment_important.xml @@ -3,4 +3,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index da8e8e6f..ecec7bc7 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,135 +1,155 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + android:orientation="vertical"> - + + + android:layout_height="match_parent" + android:fillViewport="true"> - + android:animateLayoutChanges="true" + android:gravity="center" + android:orientation="vertical" + android:padding="16dp"> + + + + + + + + android:layout_marginTop="48dp" + android:orientation="horizontal"> - + android:layout_marginTop="16dp" + android:src="@drawable/ic_baseline_account_circle_24" + app:tint="?colorAccent" /> - - - - - - - + app:boxStrokeErrorColor="@android:color/transparent"> - - - - - - - - - - - - - - + - + - - - \ No newline at end of file + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml new file mode 100644 index 00000000..72cff9af --- /dev/null +++ b/app/src/main/res/layout/fragment_main.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_validation.xml b/app/src/main/res/layout/fragment_validation.xml new file mode 100644 index 00000000..7a6e0918 --- /dev/null +++ b/app/src/main/res/layout/fragment_validation.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_conversation.xml b/app/src/main/res/layout/item_conversation.xml index ffcccacb..62273add 100644 --- a/app/src/main/res/layout/item_conversation.xml +++ b/app/src/main/res/layout/item_conversation.xml @@ -2,7 +2,7 @@ - - - - + android:src="?colorAccent" /> diff --git a/app/src/main/res/menu/activity_main_bottom.xml b/app/src/main/res/menu/activity_main_bottom.xml index b05319e7..d66816f2 100644 --- a/app/src/main/res/menu/activity_main_bottom.xml +++ b/app/src/main/res/menu/activity_main_bottom.xml @@ -2,17 +2,17 @@ diff --git a/app/src/main/res/navigation/friends.xml b/app/src/main/res/navigation/friends.xml new file mode 100644 index 00000000..1f8d3a90 --- /dev/null +++ b/app/src/main/res/navigation/friends.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/important.xml b/app/src/main/res/navigation/important.xml new file mode 100644 index 00000000..4301a41a --- /dev/null +++ b/app/src/main/res/navigation/important.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/login.xml b/app/src/main/res/navigation/login.xml new file mode 100644 index 00000000..60528bb8 --- /dev/null +++ b/app/src/main/res/navigation/login.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/main.xml b/app/src/main/res/navigation/main.xml new file mode 100644 index 00000000..2aecd907 --- /dev/null +++ b/app/src/main/res/navigation/main.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/messages.xml b/app/src/main/res/navigation/messages.xml new file mode 100644 index 00000000..e8b26d74 --- /dev/null +++ b/app/src/main/res/navigation/messages.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 91edf23a..097c40e0 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,37 +1,67 @@ - - + + + + - \ No newline at end of file diff --git a/app/src/main/res/values-v31/colors.xml b/app/src/main/res/values-v31/colors.xml new file mode 100644 index 00000000..bdde9573 --- /dev/null +++ b/app/src/main/res/values-v31/colors.xml @@ -0,0 +1,60 @@ + + + + @android:color/system_accent1_10 + @android:color/system_accent1_10 + @android:color/system_accent1_10 + @android:color/system_accent1_500 + @android:color/system_accent3_500 + @android:color/system_accent3_200 + @android:color/system_accent1_10 + + + @android:color/system_accent1_10 + + #E0E0E0 + + #99000000 + + #ff000000 + #DE000000 + #99000000 + + #ffffff + #DEFFFFFF + #99FFFFFF + + #666666 + + #F5F5F5 + + #5c5c5c + #e8e8e8 + #ffffff + + @color/accent + + #ffffff + #000000 + + + @android:color/system_accent1_1000 + @android:color/system_accent1_1000 + @android:color/system_accent1_1000 + @android:color/system_accent1_400 + @android:color/system_accent3_300 + @android:color/system_accent3_50 + @android:color/system_accent3_1000 + + @android:color/system_accent1_900 + + #292929 + + #99FFFFFF + + #272727 + + #000000 + #ffffff + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index d002566d..3bae8bfa 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,6 +1,10 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 486bd376..1152bcb5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,15 @@ - #ffffff - #ffffff - #4284F4 + + #ffffff #ffffff #ffffff + #4284F4 + #4E8CF6 + #95BBFB + #ffffff + + @color/accent #E0E0E0 @@ -35,11 +40,15 @@ - #000000 - #000000 - #63ACFF + #000000 #000000 #000000 + #63ACFF + #56A5FF + #2E90FF + #000000 + + #000000 #292929 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2bfa3a5d..3a35b892 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,8 +150,11 @@ Password E-mail or phone number Log in - Captcha + Captcha code Input code from picture + Login + + Conversations diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4fc6a9bc..20390357 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,5 +1,6 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 66230582..ccaa1221 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,15 +1,22 @@ - - - - + + + + + + + \ No newline at end of file diff --git a/arrayutils/.gitignore b/arrayutils/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/arrayutils/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/arrayutils/build.gradle b/arrayutils/build.gradle deleted file mode 100644 index b4adb37e..00000000 --- a/arrayutils/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - coreLibraryDesugaringEnabled true - - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' - - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' -} \ No newline at end of file diff --git a/arrayutils/consumer-rules.pro b/arrayutils/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/arrayutils/proguard-rules.pro b/arrayutils/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/arrayutils/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/arrayutils/src/main/AndroidManifest.xml b/arrayutils/src/main/AndroidManifest.xml deleted file mode 100644 index 5cc67c2d..00000000 --- a/arrayutils/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 084ac9a1..00000000 --- a/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -buildscript { - ext.kotlin_version = "1.4.30" - repositories { - google() - jcenter() - } - dependencies { - classpath "com.android.tools.build:gradle:4.1.2" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - flatDir { - dirs 'libs' - } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..1e4a8ca4 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,26 @@ +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20") + classpath("com.android.tools.build:gradle:7.0.1") + classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5") + + classpath("com.google.dagger:hilt-android-gradle-plugin:2.37") + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") + } +} + +tasks.register("clean", Delete::class) { + delete(rootProject.buildDir) +} \ No newline at end of file diff --git a/concurrent/.gitignore b/concurrent/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/concurrent/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/concurrent/build.gradle b/concurrent/build.gradle deleted file mode 100644 index 4894f436..00000000 --- a/concurrent/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" -} \ No newline at end of file diff --git a/concurrent/consumer-rules.pro b/concurrent/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/concurrent/proguard-rules.pro b/concurrent/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/concurrent/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/concurrent/src/main/AndroidManifest.xml b/concurrent/src/main/AndroidManifest.xml deleted file mode 100644 index 7284f3dc..00000000 --- a/concurrent/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/extensions/.gitignore b/extensions/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/extensions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/extensions/build.gradle b/extensions/build.gradle deleted file mode 100644 index 30501495..00000000 --- a/extensions/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.3.0' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' -} \ No newline at end of file diff --git a/extensions/consumer-rules.pro b/extensions/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/proguard-rules.pro b/extensions/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/extensions/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/extensions/src/main/AndroidManifest.xml b/extensions/src/main/AndroidManifest.xml deleted file mode 100644 index 9ea04e84..00000000 --- a/extensions/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a6db7c56..46b6932b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/install.bat b/install.bat new file mode 100644 index 00000000..18feb832 --- /dev/null +++ b/install.bat @@ -0,0 +1 @@ +adb install "D:\Development\Android\fast-messenger\app\build\outputs\apk\debug\app-debug.apk" \ No newline at end of file diff --git a/mvp/.gitignore b/mvp/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/mvp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/mvp/build.gradle b/mvp/build.gradle deleted file mode 100644 index d7c22f7a..00000000 --- a/mvp/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" -} \ No newline at end of file diff --git a/mvp/consumer-rules.pro b/mvp/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/mvp/proguard-rules.pro b/mvp/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/mvp/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/mvp/src/main/AndroidManifest.xml b/mvp/src/main/AndroidManifest.xml deleted file mode 100644 index 8f358f5f..00000000 --- a/mvp/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpBase.kt b/mvp/src/main/java/com/meloda/mvp/MvpBase.kt deleted file mode 100644 index a94e7e50..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpBase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.meloda.mvp - -import android.app.Application -import android.os.Handler - -object MvpBase { - - lateinit var handler: Handler - - fun init(application: Application) { - handler = Handler(application.mainLooper) - } - - fun init(appHandler: Handler) { - handler = appHandler - } - - fun post(runnable: Runnable) { - handler.post(runnable) - } -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpConstants.kt b/mvp/src/main/java/com/meloda/mvp/MvpConstants.kt deleted file mode 100644 index 8354b23a..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpConstants.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.meloda.mvp - -object MvpConstants { - - const val PEER_ID = "_peer_id" - const val ID = "_id" - const val COUNT = "_count" - const val OFFSET = "_offset" - const val FROM_CACHE = "_from_cache" - -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpException.kt b/mvp/src/main/java/com/meloda/mvp/MvpException.kt deleted file mode 100644 index 74c80d05..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.meloda.mvp - -class MvpException(var errorId: String) : Exception(errorId) \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpFields.kt b/mvp/src/main/java/com/meloda/mvp/MvpFields.kt deleted file mode 100644 index 20366e1a..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpFields.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.meloda.mvp - -import android.util.ArrayMap -import java.util.* - -@Suppress("UNCHECKED_CAST") -class MvpFields { - private val fields = ArrayMap() - - fun put(key: String, value: Any): MvpFields { - fields[key] = value - return this - } - - operator fun get(key: String): T { - return fields[key] as T - } - - fun getNonNull(key: String): Any { - return fields[key]!! - } - - fun getNonNull(`object`: Any): Any { - return Objects.requireNonNull(`object`) - } - - fun getFields(): Map { - return fields - } -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpOnLoadListener.kt b/mvp/src/main/java/com/meloda/mvp/MvpOnLoadListener.kt deleted file mode 100644 index 4e56228f..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpOnLoadListener.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.meloda.mvp - -interface MvpOnLoadListener { - - fun onSuccess() - - fun onError(t: Throwable) - -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpOnResponseListener.kt b/mvp/src/main/java/com/meloda/mvp/MvpOnResponseListener.kt deleted file mode 100644 index d274eeec..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpOnResponseListener.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.meloda.mvp - -interface MvpOnResponseListener { - - fun onResponse(response: T) - - fun onError(t: Throwable) - -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpPresenter.kt b/mvp/src/main/java/com/meloda/mvp/MvpPresenter.kt deleted file mode 100644 index 5080f35d..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpPresenter.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.meloda.mvp - -import android.content.Context -import android.os.Bundle - -@Suppress("UNCHECKED_CAST") -abstract class MvpPresenter, V : MvpView>( - protected var viewState: V, - private val repositoryStringClassName: String -) { - - protected var context: Context? = null - - protected fun requireContext(): Context { - if (context == null) throw IllegalStateException("context is null") - - return context!! - } - - enum class ListState { - EMPTY, EMPTY_LOADING, EMPTY_NO_INTERNET, EMPTY_ERROR, FILLED, FILLED_LOADING - } - - protected var tag: String = "" - - lateinit var repository: Repository - - init { - initRepository() - } - - private fun initRepository() { - val clazz = Class.forName(repositoryStringClassName) - - this.repository = clazz.newInstance() as Repository - } - - open fun onCreate(context: Context, bundle: Bundle? = null) { - this.context = context - } - - open fun onCreateView(bundle: Bundle? = null) {} - open fun onViewCreated(bundle: Bundle? = null) {} - - protected fun post(runnable: Runnable) { - MvpBase.post(runnable) - } - - open fun destroy() {} - - fun prepareViews() { - viewState.prepareNoItemsView() - viewState.prepareNoInternetView() - viewState.prepareErrorView() - } - - fun setState(state: ListState) { - when (state) { - ListState.EMPTY -> { - viewState.hideRefreshLayout() - viewState.hideProgressBar() - viewState.showNoItemsView() - viewState.hideNoInternetView() - viewState.hideErrorView() - } - ListState.EMPTY_LOADING -> { - viewState.hideRefreshLayout() - viewState.showProgressBar() - viewState.hideNoItemsView() - viewState.hideNoInternetView() - viewState.hideErrorView() - } - ListState.EMPTY_NO_INTERNET -> { - viewState.hideRefreshLayout() - viewState.hideProgressBar() - viewState.hideNoItemsView() - viewState.showNoInternetView() - viewState.hideErrorView() - } - ListState.EMPTY_ERROR -> { - viewState.hideRefreshLayout() - viewState.hideProgressBar() - viewState.hideNoItemsView() - viewState.hideNoInternetView() - viewState.showErrorView() - } - ListState.FILLED -> { - viewState.hideRefreshLayout() - viewState.hideProgressBar() - viewState.hideNoItemsView() - viewState.hideNoInternetView() - viewState.hideErrorView() - } - ListState.FILLED_LOADING -> { - viewState.showRefreshLayout() - viewState.hideProgressBar() - viewState.hideNoItemsView() - viewState.hideNoInternetView() - viewState.hideErrorView() - } - } - } - -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpRepository.kt b/mvp/src/main/java/com/meloda/mvp/MvpRepository.kt deleted file mode 100644 index 9a207abd..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpRepository.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.meloda.mvp - -@Suppress("UNCHECKED_CAST") -abstract class MvpRepository { - - protected fun sendError( - listener: MvpOnResponseListener, - t: Throwable - ) { - MvpBase.post { listener.onError(t) } - } - - protected fun sendResponseArray( - listener: MvpOnResponseListener>, - response: ArrayList - ) { - listener.let { MvpBase.handler.post { listener.onResponse(response) } } - } - - protected fun sendResponse( - listener: MvpOnResponseListener, - response: Item - ) { - listener.let { - MvpBase.handler.post { listener.onResponse(response) } - } - } -} \ No newline at end of file diff --git a/mvp/src/main/java/com/meloda/mvp/MvpView.kt b/mvp/src/main/java/com/meloda/mvp/MvpView.kt deleted file mode 100644 index 9b15d321..00000000 --- a/mvp/src/main/java/com/meloda/mvp/MvpView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.meloda.mvp - -interface MvpView { - - fun showErrorSnackbar(t: Throwable) - - fun prepareNoItemsView() - - fun showNoItemsView() - - fun hideNoItemsView() - - fun prepareNoInternetView() - - fun showNoInternetView() - - fun hideNoInternetView() - - fun prepareErrorView() - - fun showErrorView() - - fun hideErrorView() - - fun showProgressBar() - - fun hideProgressBar() - - fun showRefreshLayout() - - fun hideRefreshLayout() - -} \ No newline at end of file diff --git a/netservices/.gitignore b/netservices/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/netservices/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/netservices/build.gradle b/netservices/build.gradle deleted file mode 100644 index 30501495..00000000 --- a/netservices/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.3.0' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' -} \ No newline at end of file diff --git a/netservices/consumer-rules.pro b/netservices/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/netservices/proguard-rules.pro b/netservices/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/netservices/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/netservices/src/main/AndroidManifest.xml b/netservices/src/main/AndroidManifest.xml deleted file mode 100644 index 61783206..00000000 --- a/netservices/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index fccacc97..00000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -include ':extensions' -include ':arrayutils' -include ':concurrent' -include ':netservices' -include ':vksdk' -include ':mvp' - -include ':app' - -rootProject.name = "Fast" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..47bfbee1 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "fast-messenger" +include(":app") \ No newline at end of file diff --git a/vksdk/.gitignore b/vksdk/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/vksdk/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/vksdk/build.gradle b/vksdk/build.gradle deleted file mode 100644 index dbc0c77a..00000000 --- a/vksdk/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - - implementation 'androidx.preference:preference-ktx:1.1.1' - - implementation project(":netservices") - implementation project(":concurrent") - implementation project(":arrayutils") -} \ No newline at end of file diff --git a/vksdk/consumer-rules.pro b/vksdk/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/vksdk/proguard-rules.pro b/vksdk/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/vksdk/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/vksdk/src/main/AndroidManifest.xml b/vksdk/src/main/AndroidManifest.xml deleted file mode 100644 index 689d0552..00000000 --- a/vksdk/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file