From 95deed58bb18cfa3bcbb04b67a63746ca959eb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9luchu?= Date: Sun, 15 Nov 2020 15:42:36 +0100 Subject: [PATCH] v2.0.0 - New Desing, improve and fix errors --- app/build.gradle | 81 ------- app/build.gradle.kts | 87 +++++++ app/proguard-rules.pro | 2 +- app/src/main/AndroidManifest.xml | 15 +- .../wastickersonline/WaStickersOnline.kt | 6 +- .../core/database/AppDatabase.kt | 41 ++++ .../wastickersonline/core/di/Database.kt | 8 + .../wastickersonline/core/di/Modules.kt | 6 +- .../wastickersonline/core/di/Repositories.kt | 3 +- .../core/exception/Failure.kt | 2 +- .../extensions/dateAndTime/DateExtensions.kt | 5 + .../core/extensions/hawk/HawkExtensions.kt | 6 + .../core/extensions/koin/KoinExtensions.kt | 6 +- .../core/extensions/others/OtherExensions.kt | 107 +++++++++ .../extensions/retrofit/RetrofitClient.kt | 44 ---- .../sharedprefs/SharedPrefsExceptions.kt | 3 + .../sharedprefs/SharedPrefsExtensions.kt | 5 + .../sharedprefs/SharedPrefsHelpers.kt | 183 +++++++++++++++ .../core/functional/DialogCallback.kt | 6 - .../core/interactor/UseCaseWithout.kt | 17 -- .../core/utils/AnimatedRecyclerView.kt | 160 +++++++++++++ .../wastickersonline/core/utils/Constants.kt | 9 + .../core/utils/ConstantsMeth.kt | 9 + .../core/utils/hawk/Converter.java | 8 + .../core/utils/hawk/DataInfo.java | 21 ++ .../core/utils/hawk/DefaultHawkFacade.java | 114 +++++++++ .../core/utils/hawk/GsonParser.java | 31 +++ .../core/utils/hawk/Hawk.java | 49 ++++ .../core/utils/hawk/HawkBuilder.java | 63 +++++ .../core/utils/hawk/HawkConverter.java | 112 +++++++++ .../core/utils/hawk/HawkFacade.java | 67 ++++++ .../core/utils/hawk/HawkSerializer.java | 102 ++++++++ .../core/utils/hawk/HawkUtils.java | 23 ++ .../core/utils/hawk/LogInterceptor.java | 5 + .../core/utils/hawk/Parser.java | 10 + .../core/utils/hawk/Serializer.java | 8 + .../utils/hawk/SharedPreferencesStorage.java | 45 ++++ .../core/utils/hawk/Storage.java | 12 + .../core/utils/room/ListStringConverter.kt | 29 +++ .../features/sticker/models/StickerEntity.kt | 1 - .../sticker/models/StickerPackEntity.kt | 63 +++-- .../sticker/models/StickerPackView.kt | 5 +- .../provider/StickerContentProvider.kt | 26 +-- .../sticker/repository/StickersRepository.kt | 33 ++- .../sticker/repository/StickersService.kt | 8 +- .../repository/local/StickerDBLocal.kt | 9 + .../sticker/repository/local/StickerLocal.kt | 17 ++ .../sticker/repository/local/StickersDAO.kt | 21 ++ .../features/sticker/view/MainActivity.kt | 68 ++---- .../sticker/view/StickerDetailsActivity.kt | 171 ++++++++------ .../{ => view}/adapter/StickersAdapter.kt | 6 +- .../adapter/StickersDetailsAdapter.kt | 8 +- app/src/main/res/anim/enter_slide_bottom.xml | 9 + app/src/main/res/anim/enter_slide_left.xml | 9 + app/src/main/res/anim/enter_slide_right.xml | 9 + app/src/main/res/anim/exit_slide_bottom.xml | 9 + app/src/main/res/anim/exit_slide_left.xml | 9 + app/src/main/res/anim/exit_slide_right.xml | 9 + .../res/anim/item_animation_from_bottom.xml | 15 ++ .../anim/item_animation_from_bottom_scale.xml | 24 ++ .../res/anim/layout_animation_from_bottom.xml | 5 + .../layout_animation_from_bottom_scale.xml | 5 + app/src/main/res/drawable/ic_arrow_left.xml | 9 + app/src/main/res/layout/activity_main.xml | 52 +++-- .../res/layout/activity_sticker_details.xml | 220 +++++++++++++++--- ...ist_item_image.xml => item_stickimage.xml} | 3 +- app/src/main/res/values/attrs.xml | 19 ++ app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimens.xml | 19 ++ app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/styles.xml | 21 ++ build.gradle | 24 -- build.gradle.kts | 21 ++ settings.gradle | 1 - settings.gradle.kts | 1 + 75 files changed, 2039 insertions(+), 406 deletions(-) delete mode 100644 app/build.gradle create mode 100644 app/build.gradle.kts create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/database/AppDatabase.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/di/Database.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/dateAndTime/DateExtensions.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/hawk/HawkExtensions.kt delete mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/retrofit/RetrofitClient.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExceptions.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExtensions.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsHelpers.kt delete mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/functional/DialogCallback.kt delete mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/interactor/UseCaseWithout.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/AnimatedRecyclerView.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/Constants.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/ConstantsMeth.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Converter.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DataInfo.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DefaultHawkFacade.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/GsonParser.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Hawk.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkBuilder.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkConverter.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkFacade.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkSerializer.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkUtils.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/LogInterceptor.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Parser.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Serializer.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/SharedPreferencesStorage.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Storage.java create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/core/utils/room/ListStringConverter.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerDBLocal.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerLocal.kt create mode 100644 app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickersDAO.kt rename app/src/main/java/com/jeluchu/wastickersonline/features/sticker/{ => view}/adapter/StickersAdapter.kt (96%) rename app/src/main/java/com/jeluchu/wastickersonline/features/sticker/{ => view}/adapter/StickersDetailsAdapter.kt (85%) create mode 100644 app/src/main/res/anim/enter_slide_bottom.xml create mode 100644 app/src/main/res/anim/enter_slide_left.xml create mode 100644 app/src/main/res/anim/enter_slide_right.xml create mode 100644 app/src/main/res/anim/exit_slide_bottom.xml create mode 100644 app/src/main/res/anim/exit_slide_left.xml create mode 100644 app/src/main/res/anim/exit_slide_right.xml create mode 100644 app/src/main/res/anim/item_animation_from_bottom.xml create mode 100644 app/src/main/res/anim/item_animation_from_bottom_scale.xml create mode 100644 app/src/main/res/anim/layout_animation_from_bottom.xml create mode 100644 app/src/main/res/anim/layout_animation_from_bottom_scale.xml create mode 100644 app/src/main/res/drawable/ic_arrow_left.xml rename app/src/main/res/layout/{list_item_image.xml => item_stickimage.xml} (85%) create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/dimens.xml delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index dd8d159..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion 30 - defaultConfig { - applicationId "com.jeluchu.wastickersonline" - minSdkVersion 16 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - vectorDrawables.useSupportLibrary = true - multiDexEnabled true - def contentProviderAuthority = applicationId + ".provider.StickerContentProvider" - manifestPlaceholders = [contentProviderAuthority: contentProviderAuthority] - buildConfigField("String", "CONTENT_PROVIDER_AUTHORITY", "\"${contentProviderAuthority}\"") - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() - } - buildTypes { - debug { - debuggable false - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'androidx.cardview:cardview:1.0.0' - implementation 'com.github.bumptech.glide:glide:4.11.0' - - annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' - implementation 'androidx.multidex:multidex:2.0.1' - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - - implementation 'com.orhanobut:hawk:2.0.1' - implementation 'io.coil-kt:coil:0.11.0' - - // KOTLIN LIBRARY ------------------------------------------------------------------------------ - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72' - implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7' - implementation 'androidx.core:core-ktx:1.3.1' - - // KOIN LIBRARY -------------------------------------------------------------------------------- - implementation 'org.koin:koin-android:2.1.5' - implementation 'org.koin:koin-android-viewmodel:2.1.5' - implementation 'org.koin:koin-android-scope:2.1.5' - - // LIFECYCLE LIBRARY --------------------------------------------------------------------------- - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' - - // RETROFIT ------------------------------------------------------------------------------------ - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' - implementation 'com.google.code.gson:gson:2.8.6' - implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2' - -} - -repositories { - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } - mavenCentral() -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..1caf417 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,87 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-android-extensions") + id("kotlin-kapt") +} + +android { + compileSdkVersion(30) + defaultConfig { + applicationId = "com.jeluchu.wastickersonline" + minSdkVersion(21) + targetSdkVersion(30) + versionCode = 1 + versionName = "2.0.0" + vectorDrawables.useSupportLibrary = true + val contentProviderAuthority = "$applicationId.provider.StickerContentProvider" + manifestPlaceholders = mapOf("contentProviderAuthority" to contentProviderAuthority) + buildConfigField("String", "CONTENT_PROVIDER_AUTHORITY", "\"${contentProviderAuthority}\"") + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + buildTypes { + getByName("debug") { + isDebuggable = true + isMinifyEnabled = false + isShrinkResources = false + isZipAlignEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + getByName("release") { + isDebuggable = false + isMinifyEnabled = true + isShrinkResources = true + isZipAlignEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } +} + +dependencies { + + // GOOGLE LIBRARY ------------------------------------------------------------------------------ + implementation("androidx.appcompat:appcompat:1.2.0") + implementation("androidx.constraintlayout:constraintlayout:2.0.4") + implementation("com.google.android.material:material:1.2.1") + implementation("androidx.preference:preference-ktx:1.1.1") + + // KOTLIN LIBRARY ------------------------------------------------------------------------------ + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10") + implementation("org.jetbrains.kotlin:kotlin-reflect:1.4.10") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1") + implementation("androidx.core:core-ktx:1.3.2") + + // KOIN LIBRARY -------------------------------------------------------------------------------- + implementation("org.koin:koin-androidx-scope:2.1.6") + implementation("org.koin:koin-androidx-viewmodel:2.1.6") + implementation("org.koin:koin-android-ext:2.1.6") + + // LIFECYCLE LIBRARY --------------------------------------------------------------------------- + implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") + implementation("androidx.lifecycle:lifecycle-common-java8:2.2.0") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0") + + // ROOM LIBRARY -------------------------------------------------------------------------------- + implementation("androidx.room:room-runtime:2.2.5") + implementation("androidx.browser:browser:1.2.0") + kapt("androidx.room:room-compiler:2.2.5") + + // RETROFIT ------------------------------------------------------------------------------------ + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") + implementation("com.google.code.gson:gson:2.8.6") + implementation("com.squareup.okhttp3:logging-interceptor:4.9.0") + + // MULTIMEDIA ---------------------------------------------------------------------------------- + implementation("io.coil-kt:coil:1.0.0") + +} + +repositories { mavenCentral() } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f1b4245..2f9dc5a 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/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7fdb886..e729cef 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,16 @@ + + + + - - - \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/WaStickersOnline.kt b/app/src/main/java/com/jeluchu/wastickersonline/WaStickersOnline.kt index a673bf5..0d1bc85 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/WaStickersOnline.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/WaStickersOnline.kt @@ -1,15 +1,17 @@ package com.jeluchu.wastickersonline import android.app.Application +import com.jeluchu.wastickersonline.core.extensions.hawk.initHawk import com.jeluchu.wastickersonline.core.extensions.koin.initKoin -import com.orhanobut.hawk.Hawk +import com.jeluchu.wastickersonline.core.extensions.sharedprefs.initSharedPrefs class WaStickersOnline : Application() { override fun onCreate() { super.onCreate() initKoin() + initSharedPrefs() + initHawk() - Hawk.init(this).build() } } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/database/AppDatabase.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/database/AppDatabase.kt new file mode 100644 index 0000000..f7b283f --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/database/AppDatabase.kt @@ -0,0 +1,41 @@ +package com.jeluchu.wastickersonline.core.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.jeluchu.wastickersonline.core.utils.room.ListStringConverter +import com.jeluchu.wastickersonline.features.sticker.models.StickerPackEntity +import com.jeluchu.wastickersonline.features.sticker.repository.local.StickersDAO + +@Database( + entities = [ + StickerPackEntity::class, + ], version = 1, exportSchema = false) +@TypeConverters(value = [ListStringConverter::class]) +abstract class AppDatabase : RoomDatabase() { + + abstract fun stickerEntityDao(): StickersDAO + + companion object { + @Volatile + private var INSTANCE: AppDatabase? = null + + fun getAppDatabase(context: Context): AppDatabase { + + return INSTANCE ?: synchronized(this) { + val instance = + Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "waStickersDB") + .allowMainThreadQueries() + .fallbackToDestructiveMigration() + .build() + INSTANCE = instance + instance + } + } + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/di/Database.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Database.kt new file mode 100644 index 0000000..980685e --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Database.kt @@ -0,0 +1,8 @@ +package com.jeluchu.wastickersonline.core.di + +import com.jeluchu.wastickersonline.features.sticker.repository.local.StickerLocal +import org.koin.dsl.module + +val databaseModule = module { + factory { StickerLocal(get()) } +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/di/Modules.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Modules.kt index fbf621e..3bbfa24 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/di/Modules.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Modules.kt @@ -1,10 +1,9 @@ package com.jeluchu.wastickersonline.core.di -import com.jeluchu.wastickersonline.features.sticker.adapter.StickersDetailsAdapter import com.jeluchu.wastickersonline.core.extensions.others.ContextHandler -import com.jeluchu.wastickersonline.core.extensions.retrofit.RetrofitClient import com.jeluchu.wastickersonline.core.platform.NetworkHandler -import com.jeluchu.wastickersonline.features.sticker.adapter.StickersAdapter +import com.jeluchu.wastickersonline.features.sticker.view.adapter.StickersAdapter +import com.jeluchu.wastickersonline.features.sticker.view.adapter.StickersDetailsAdapter import kotlinx.coroutines.Dispatchers import org.koin.dsl.module import retrofit2.Retrofit @@ -13,7 +12,6 @@ import retrofit2.converter.gson.GsonConverterFactory val networkModule = module { factory { ContextHandler(get()) } factory { NetworkHandler(get()) } - single { RetrofitClient.buildRetrofit("https://aruppi.jeluchu.xyz/res/stickers/", GsonConverterFactory.create()) } single { Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()) } factory { Dispatchers.IO } } diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/di/Repositories.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Repositories.kt index 60cb8d2..35648ac 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/di/Repositories.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/di/Repositories.kt @@ -4,6 +4,5 @@ import com.jeluchu.wastickersonline.features.sticker.repository.StickersReposito import org.koin.dsl.module val repositoryModule = module { - factory { StickersRepository.Network(get(), get()) } - + factory { StickersRepository.Network(get(), get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/exception/Failure.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/exception/Failure.kt index d536fd9..dc73f6f 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/exception/Failure.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/exception/Failure.kt @@ -1,7 +1,7 @@ package com.jeluchu.wastickersonline.core.exception sealed class Failure { - class NetworkConnection: Failure() + class NetworkConnection : Failure() class ServerError : Failure() data class CustomError(val errorCode: Int, val errorMessage: String?) : Failure() } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/dateAndTime/DateExtensions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/dateAndTime/DateExtensions.kt new file mode 100644 index 0000000..979e177 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/dateAndTime/DateExtensions.kt @@ -0,0 +1,5 @@ +package com.jeluchu.wastickersonline.core.extensions.dateAndTime + +import java.util.* + +fun isFetchSixHours(lastFetchTime: Long): Boolean = Date().time - lastFetchTime >= 6*60*60*1000 diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/hawk/HawkExtensions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/hawk/HawkExtensions.kt new file mode 100644 index 0000000..77b1229 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/hawk/HawkExtensions.kt @@ -0,0 +1,6 @@ +package com.jeluchu.wastickersonline.core.extensions.hawk + +import android.content.Context +import com.jeluchu.wastickersonline.core.utils.hawk.Hawk + +fun Context.initHawk() = Hawk.init(this).build() \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/koin/KoinExtensions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/koin/KoinExtensions.kt index 14165dc..a140349 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/koin/KoinExtensions.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/koin/KoinExtensions.kt @@ -10,13 +10,17 @@ fun Context.initKoin() { startKoin { androidLogger() androidContext(this@initKoin) - modules(listOf( + + koin.loadModules (listOf( networkModule, + databaseModule, applicationModule, dataSourceModule, repositoryModule, useCaseModule, viewModelModule )) + koin.createRootScope () + //modules() (temporarily off) } } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/others/OtherExensions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/others/OtherExensions.kt index 1101b83..27151e1 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/others/OtherExensions.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/others/OtherExensions.kt @@ -1,14 +1,121 @@ package com.jeluchu.wastickersonline.core.extensions.others import android.annotation.SuppressLint +import android.app.Activity +import android.app.PendingIntent import android.content.Context import android.content.Context.CONNECTIVITY_SERVICE +import android.content.Intent +import android.graphics.BitmapFactory +import android.graphics.Color import android.net.ConnectivityManager import android.net.NetworkCapabilities +import android.net.Uri import android.os.Build +import android.os.Bundle +import android.provider.Browser import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.WindowManager +import android.widget.TextView +import androidx.annotation.ColorRes +import androidx.annotation.RequiresApi +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat +import com.jeluchu.wastickersonline.R +import java.io.IOException + +inline val buildIsMarshmallowAndUp: Boolean + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + +inline val buildIsLollipopAndUp: Boolean + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 + +fun Activity.statusBarColor() { + setStatusBarColor(R.color.white) + setSystemBarLight(this) +} + +@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) +fun Activity.setStatusBarColor(@ColorRes color: Int) { + if (buildIsLollipopAndUp) { + val window = window + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + window.statusBarColor = ContextCompat.getColor(this, color) + } +} + +fun setSystemBarLight(act: Activity) { + if (buildIsMarshmallowAndUp) { + val view = act.findViewById(android.R.id.content) + var flags = view.systemUiVisibility + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + } + view.systemUiVisibility = flags + } +} + +fun Context.openActivity(it: Class, extras: Bundle.() -> Unit = {}) { + val intent = Intent(this, it) + intent.putExtras(Bundle().apply(extras)) + startActivity(intent) +} + +fun Activity.openActivityRight() { + overridePendingTransition(R.anim.enter_slide_right, R.anim.exit_slide_left) +} + +fun Activity.exitActivityBottom() { + finish() + overridePendingTransition(R.anim.enter_slide_bottom, R.anim.exit_slide_bottom) +} + +fun Activity.exitActivityLeft() { + finish() + overridePendingTransition(R.anim.enter_slide_left, R.anim.exit_slide_right) +} + +fun Activity.openInCustomTab(string: String) = customTabsWeb(string) + +private fun Context.customTabsWeb(string: String) { + try { + + val builder = CustomTabsIntent.Builder() + + builder.setToolbarColor(Color.parseColor("#"+Integer.toHexString(ContextCompat.getColor(this, R.color.redCardBackground)))) + builder.setShowTitle(true) + builder.setExitAnimations(this, R.anim.enter_slide_left, R.anim.exit_slide_left) + builder.setStartAnimations(this, R.anim.enter_slide_right, R.anim.exit_slide_right) + + val intent = builder.build() + intent.launchUrl(this, Uri.parse(string)) + + } + catch (e: IOException) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(string)) + intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true) + intent.putExtra(Browser.EXTRA_APPLICATION_ID, packageName) + startActivity(intent) + } +} + +fun noCrash(enableLog: Boolean = true, func: () -> Unit): String? { + return try { + func() + null + } catch (e: Exception) { + if (enableLog) + e.printStackTrace() + e.message + } +} + +fun TextView.simpletext(value: String) { + this.text = value +} fun getLastBitFromUrl(url: String): String = url.replaceFirst(".*/([^/?]+).*".toRegex(), "$1") diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/retrofit/RetrofitClient.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/retrofit/RetrofitClient.kt deleted file mode 100644 index 5f7891b..0000000 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/retrofit/RetrofitClient.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.jeluchu.wastickersonline.core.extensions.retrofit - -import com.jeluchu.wastickersonline.BuildConfig -import okhttp3.ConnectionPool -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Converter -import retrofit2.Retrofit -import java.util.concurrent.TimeUnit - -object RetrofitClient { - - private var CONNECT_TIMEOUT = 60L - private var READ_TIMEOUT = 60L - private var WRITE_TIMEOUT = 60L - - fun buildRetrofit(baseUrl: String, converterFactory: Converter.Factory): Retrofit? { - return Retrofit.Builder() - .baseUrl(baseUrl) - .client(createClientBuilder().build()) - .addConverterFactory(converterFactory) - .build() - } - - private fun createClientBuilder(): OkHttpClient.Builder { - val clientBuilder = OkHttpClient.Builder().connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) - .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) - .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) - .retryOnConnectionFailure(true) - .connectionPool(ConnectionPool(0, 1, TimeUnit.NANOSECONDS)) - if (BuildConfig.DEBUG) { - clientBuilder.addInterceptor(makeLoggingInterceptor()) - } - return clientBuilder - } - - private fun makeLoggingInterceptor(): HttpLoggingInterceptor { - val debugInterceptor = HttpLoggingInterceptor() - debugInterceptor.level = HttpLoggingInterceptor.Level.BODY - return debugInterceptor - } - -} - diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExceptions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExceptions.kt new file mode 100644 index 0000000..acb90dc --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExceptions.kt @@ -0,0 +1,3 @@ +package com.jeluchu.wastickersonline.core.extensions.sharedprefs + +class SharedPrefsExceptions(message: String?) : RuntimeException(message) \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExtensions.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExtensions.kt new file mode 100644 index 0000000..594076d --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsExtensions.kt @@ -0,0 +1,5 @@ +package com.jeluchu.wastickersonline.core.extensions.sharedprefs + +import android.content.Context + +fun Context.initSharedPrefs() = SharedPrefsHelpers.init(this) \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsHelpers.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsHelpers.kt new file mode 100644 index 0000000..3d4d98a --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/extensions/sharedprefs/SharedPrefsHelpers.kt @@ -0,0 +1,183 @@ +package com.jeluchu.wastickersonline.core.extensions.sharedprefs + +import android.content.Context +import android.content.SharedPreferences +import android.util.Log +import androidx.preference.PreferenceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken + + +class SharedPrefsHelpers { + fun saveInt(key: String?, value: Int) { + val editor = mSharedPreferences!!.edit() + editor.putInt(key, value) + editor.apply() + } + + fun getInt(key: String, defaultValue: Int): Int { + return if (isKeyExists(key)) { + mSharedPreferences!!.getInt(key, defaultValue) + } else defaultValue + } + + fun saveBoolean(key: String?, value: Boolean) { + val editor = mSharedPreferences!!.edit() + editor.putBoolean(key, value) + editor.apply() + } + + fun getBoolean(key: String, defaultValue: Boolean): Boolean { + return if (isKeyExists(key)) { + mSharedPreferences!!.getBoolean(key, defaultValue) + } else defaultValue + } + + fun saveFloat(key: String?, value: Float) { + val editor = mSharedPreferences!!.edit() + editor.putFloat(key, value) + editor.apply() + } + + fun getFloat(key: String, defaultValue: Float): Float { + return if (isKeyExists(key)) { + mSharedPreferences!!.getFloat(key, defaultValue) + } else defaultValue + } + + fun saveLong(key: String?, value: Long) { + val editor = mSharedPreferences!!.edit() + editor.putLong(key, value) + editor.apply() + } + + fun getLong(key: String, defaultValue: Long): Long { + return if (isKeyExists(key)) { + mSharedPreferences!!.getLong(key, defaultValue) + } else defaultValue + } + + fun saveString(key: String?, value: String?) { + val editor = mSharedPreferences!!.edit() + editor.putString(key, value) + editor.apply() + } + + fun getString(key: String, defaultValue: String?): String? { + return if (isKeyExists(key)) { + mSharedPreferences!!.getString(key, defaultValue) + } else defaultValue + } + + fun saveObject(key: String?, `object`: T) { + val objectString = Gson().toJson(`object`) + val editor = mSharedPreferences!!.edit() + editor.putString(key, objectString) + editor.apply() + } + + fun getObject(key: String, classType: Class?): T? { + if (isKeyExists(key)) { + val objectString = mSharedPreferences!!.getString(key, null) + if (objectString != null) { + return Gson().fromJson(objectString, classType) + } + } + return null + } + + fun saveObjectsList(key: String?, objectList: List?) { + val objectString = Gson().toJson(objectList) + val editor = mSharedPreferences!!.edit() + editor.putString(key, objectString) + editor.apply() + } + + fun getObjectsList(key: String, classType: Class?): List? { + if (isKeyExists(key)) { + val objectString = mSharedPreferences!!.getString(key, null) + if (objectString != null) { + val t: ArrayList = Gson().fromJson(objectString, object : TypeToken?>() {}.type) + val finalList: MutableList = ArrayList() + for (i in 0 until t.size) { + finalList.add(t[i]) + } + return finalList + } + } + return null + } + + + fun setDataList(tag: String?, datalist: List?) { + if (null == datalist || datalist.isEmpty()) return + val gson = Gson() + val editor = mSharedPreferences!!.edit() + + val strJson = gson.toJson(datalist) + editor.clear() + editor.putString(tag, strJson) + editor.apply() + } + + + fun getDataList(tag: String?): List? { + var datalist: List = ArrayList() + val strJson: String = mSharedPreferences!!.getString(tag, null) ?: return datalist + val gson = Gson() + datalist = gson.fromJson(strJson, object : TypeToken?>() {}.type) + return datalist + } + + + fun clearSession() { + val editor = mSharedPreferences!!.edit() + editor.clear() + editor.apply() + } + + fun deleteValue(key: String): Boolean { + if (isKeyExists(key)) { + val editor = mSharedPreferences!!.edit() + editor.remove(key) + editor.apply() + return true + } + return false + } + + fun isKeyExists(key: String): Boolean { + val map = mSharedPreferences!!.all + return if (map.containsKey(key)) { + true + } else { + Log.e("SharedPreferences", "No element founded in sharedPrefs with the key $key") + false + } + } + + companion object { + var instance: SharedPrefsHelpers? = null + get() { + if (field == null) { + validateInitialization() + synchronized(SharedPrefsHelpers::class.java) { + if (field == null) { + field = SharedPrefsHelpers() + } + } + } + return field + } + private set + private var mSharedPreferences: SharedPreferences? = null + + fun init(context: Context?) { + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + } + + private fun validateInitialization() { + if (mSharedPreferences == null) throw SharedPrefsExceptions("SharedPreferencesHelpers must be initialized inside your application class by calling SharedPreferencesHelpers.init(getApplicationContext)") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/functional/DialogCallback.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/functional/DialogCallback.kt deleted file mode 100644 index 8283b09..0000000 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/functional/DialogCallback.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.jeluchu.wastickersonline.core.functional - -interface DialogCallback { - fun onAccept() - fun onDecline() -} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/interactor/UseCaseWithout.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/interactor/UseCaseWithout.kt deleted file mode 100644 index 664321e..0000000 --- a/app/src/main/java/com/jeluchu/wastickersonline/core/interactor/UseCaseWithout.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.jeluchu.wastickersonline.core.interactor - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.launch - -abstract class UseCaseWithout{ - abstract suspend fun run(params: Params) - - operator fun invoke(params: Params) { - GlobalScope.launch(Dispatchers.Main) { - val job = async(Dispatchers.IO) {run(params)} - job.await() - } - } -} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/AnimatedRecyclerView.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/AnimatedRecyclerView.kt new file mode 100644 index 0000000..f69acfa --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/AnimatedRecyclerView.kt @@ -0,0 +1,160 @@ +package com.jeluchu.wastickersonline.core.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.animation.AnimationUtils +import android.view.animation.LayoutAnimationController +import androidx.annotation.AnimRes +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.jeluchu.wastickersonline.R + +class AnimatedRecyclerView : RecyclerView { + private var orientation = LinearLayoutManager.VERTICAL + private var reverse = false + private var animationDuration = 600 + private var layoutManagerType = LayoutManagerType.LINEAR + private var columns = 1 + + @AnimRes + private var animation = R.anim.layout_animation_from_bottom + private var animationController: LayoutAnimationController? = null + + constructor( + context: Context, orientation: Int, reverse: Boolean, + animationDuration: Int, layoutManagerType: Int, columns: Int, + animation: Int, animationController: LayoutAnimationController? + ) : super(context) { + this.orientation = orientation + this.reverse = reverse + this.animationDuration = animationDuration + this.layoutManagerType = layoutManagerType + this.columns = columns + this.animation = animation + this.animationController = animationController + init(context, null) + } + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super( + context, + attrs, + defStyle + ) { + init(context, attrs) + } + + @SuppressLint("Recycle", "WrongConstant") + private fun init(context: Context, attrs: AttributeSet?) { + val typedArray = + context.obtainStyledAttributes(attrs, R.styleable.AnimatedRecyclerView, 0, 0) + orientation = typedArray.getInt( + R.styleable.AnimatedRecyclerView_layoutManagerOrientation, + orientation + ) + reverse = + typedArray.getBoolean(R.styleable.AnimatedRecyclerView_layoutManagerReverse, reverse) + animationDuration = + typedArray.getInt(R.styleable.AnimatedRecyclerView_animationDuration, animationDuration) + layoutManagerType = + typedArray.getInt(R.styleable.AnimatedRecyclerView_layoutManagerType, layoutManagerType) + columns = + typedArray.getInt(R.styleable.AnimatedRecyclerView_gridLayoutManagerColumns, columns) + animation = typedArray.getResourceId(R.styleable.AnimatedRecyclerView_layoutAnimation, -1) + if (animationController == null) animationController = + if (animation != -1) AnimationUtils.loadLayoutAnimation( + getContext(), + animation + ) else AnimationUtils.loadLayoutAnimation( + getContext(), + R.anim.layout_animation_from_bottom + ) + animationController!!.animation.duration = animationDuration.toLong() + layoutAnimation = animationController + if (layoutManagerType == LayoutManagerType.LINEAR) layoutManager = LinearLayoutManager( + context, + orientation, + reverse + ) else if (layoutManagerType == LayoutManagerType.GRID) layoutManager = + GridLayoutManager(context, columns, orientation, reverse) + } + + class Builder(private val context: Context) { + private var orientation = LinearLayoutManager.VERTICAL + private var reverse = false + private var animationDuration = 600 + private var layoutManagerType = LayoutManagerType.LINEAR + private var columns = 1 + + @AnimRes + private var animation = R.anim.layout_animation_from_bottom + private var animationController: LayoutAnimationController? = null + fun orientation(orientation: Int): Builder { + this.orientation = orientation + return this + } + + fun reverse(reverse: Boolean): Builder { + this.reverse = reverse + return this + } + + fun animationDuration(animationDuration: Int): Builder { + this.animationDuration = animationDuration + return this + } + + fun layoutManagerType(@LayoutManagerType layoutManagerType: Int): Builder { + this.layoutManagerType = layoutManagerType + return this + } + + fun columns(columns: Int): Builder { + this.columns = columns + return this + } + + fun animation(@AnimRes animation: Int): Builder { + this.animation = animation + return this + } + + fun animationController(animationController: LayoutAnimationController?): Builder { + this.animationController = animationController + return this + } + + fun build(): AnimatedRecyclerView { + return AnimatedRecyclerView( + context, orientation, reverse, animationDuration, layoutManagerType, columns, + animation, animationController + ) + } + } + + @Throws(Exception::class) + fun notifyDataSetChanged() { + if (adapter != null) { + adapter!!.notifyDataSetChanged() + scheduleLayoutAnimation() + } else { + throw Exception("The adapter must be set") + } + } + + annotation class LayoutManagerType { + companion object { + var LINEAR = 0 + var GRID = 1 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/Constants.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/Constants.kt new file mode 100644 index 0000000..1578816 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/Constants.kt @@ -0,0 +1,9 @@ +package com.jeluchu.wastickersonline.core.utils + +object LocalShared { + + object Stickers { + const val stickers = "stickers" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/ConstantsMeth.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/ConstantsMeth.kt new file mode 100644 index 0000000..08f1196 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/ConstantsMeth.kt @@ -0,0 +1,9 @@ +package com.jeluchu.wastickersonline.core.utils + +class ConstantsMeth { + + companion object { + private var ENDPOINT_STICKERS: String = "https://aruppi.jeluchu.xyz/res/stickers/" + fun getApiEndpointStickers(): String = ENDPOINT_STICKERS + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Converter.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Converter.java new file mode 100644 index 0000000..153b1fd --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Converter.java @@ -0,0 +1,8 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public interface Converter { + + String toString(T value); + T fromString(String value, DataInfo dataInfo) throws Exception; + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DataInfo.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DataInfo.java new file mode 100644 index 0000000..22e5a83 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DataInfo.java @@ -0,0 +1,21 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +final class DataInfo { + + static final char TYPE_OBJECT = '0'; + static final char TYPE_LIST = '1'; + static final char TYPE_MAP = '2'; + static final char TYPE_SET = '3'; + + final char dataType; + final String cipherText; + final Class keyClazz; + final Class valueClazz; + + DataInfo(char dataType, String cipherText, Class keyClazz, Class valueClazz) { + this.cipherText = cipherText; + this.keyClazz = keyClazz; + this.valueClazz = valueClazz; + this.dataType = dataType; + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DefaultHawkFacade.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DefaultHawkFacade.java new file mode 100644 index 0000000..da42337 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/DefaultHawkFacade.java @@ -0,0 +1,114 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public class DefaultHawkFacade implements HawkFacade { + + private final Storage storage; + private final Converter converter; + private final Serializer serializer; + private final LogInterceptor logInterceptor; + + public DefaultHawkFacade(HawkBuilder builder) { + storage = builder.getStorage(); + converter = builder.getConverter(); + serializer = builder.getSerializer(); + logInterceptor = builder.getLogInterceptor(); + } + + @Override + public boolean put(String key, T value) { + + HawkUtils.checkNull("Key", key); + log("Hawk.put -> key: " + key + ", value: " + value); + + if (value == null) { + log("Hawk.put -> Value is null. Any existing value will be deleted with the given key"); + return delete(key); + } + + String plainText = converter.toString(value); + log("Hawk.put -> Converted to " + plainText); + if (plainText == null) { + log("Hawk.put -> Converter failed"); + return false; + } + + String serializedText = serializer.serialize(plainText, value); + log("Hawk.put -> Serialized to " + serializedText); + if (serializedText == null) { + log("Hawk.put -> Serialization failed"); + return false; + } + + if (storage.put(key, serializedText)) { + log("Hawk.put -> Stored successfully"); + return true; + } else { + log("Hawk.put -> Store operation failed"); + return false; + } + } + + @Override + public T get(String key) { + log("Hawk.get -> key: " + key); + if (key == null) { + log("Hawk.get -> null key, returning null value "); + return null; + } + + String serializedText = storage.get(key); + log("Hawk.get -> Fetched from storage : " + serializedText); + if (serializedText == null) { + log("Hawk.get -> Fetching from storage failed"); + return null; + } + + DataInfo dataInfo = serializer.deserialize(serializedText); + log("Hawk.get -> Deserialized"); + if (dataInfo == null) { + log("Hawk.get -> Deserialization failed"); + return null; + } + + T result = null; + try { + result = converter.fromString(dataInfo.cipherText, dataInfo); + log("Hawk.get -> Converted to : " + result); + } catch (Exception e) { + log("Hawk.get -> Converter failed"); + } + + return result; + } + + @Override + public T get(String key, T defaultValue) { + T t = get(key); + if (t == null) return defaultValue; + return t; + } + + @Override + public boolean deleteAll() { + return storage.deleteAll(); + } + + @Override + public boolean delete(String key) { + return storage.delete(key); + } + + @Override + public boolean contains(String key) { + return storage.contains(key); + } + + @Override + public boolean isBuilt() { + return true; + } + + private void log(String message) { + logInterceptor.onLog(message); + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/GsonParser.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/GsonParser.java new file mode 100644 index 0000000..8b47988 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/GsonParser.java @@ -0,0 +1,31 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; + +public final class GsonParser implements Parser { + + private final Gson gson; + + public GsonParser(Gson gson) { + this.gson = gson; + } + + @Override + public T fromJson(String content, Type type) throws JsonSyntaxException { + if (TextUtils.isEmpty(content)) { + return null; + } + return gson.fromJson(content, type); + } + + @Override + public String toJson(Object body) { + return gson.toJson(body); + } + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Hawk.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Hawk.java new file mode 100644 index 0000000..7cd9a5c --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Hawk.java @@ -0,0 +1,49 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import android.content.Context; + +public final class Hawk { + + private Hawk() { } + + static HawkFacade hawkFacade = new HawkFacade.EmptyHawkFacade(); + + public static HawkBuilder init(Context context) { + HawkUtils.checkNull("Context", context); + hawkFacade = null; + return new HawkBuilder(context); + } + + static void build(HawkBuilder hawkBuilder) { + hawkFacade = new DefaultHawkFacade(hawkBuilder); + } + + public static boolean put(String key, T value) { + return hawkFacade.put(key, value); + } + + public static T get(String key) { + return hawkFacade.get(key); + } + + public static T get(String key, T defaultValue) { + return hawkFacade.get(key, defaultValue); + } + + public static boolean deleteAll() { + return hawkFacade.deleteAll(); + } + + public static boolean delete(String key) { + return hawkFacade.delete(key); + } + + public static boolean contains(String key) { + return hawkFacade.contains(key); + } + + public static boolean isBuilt() { + return hawkFacade.isBuilt(); + } + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkBuilder.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkBuilder.java new file mode 100644 index 0000000..7de622a --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkBuilder.java @@ -0,0 +1,63 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import android.content.Context; + +import com.google.gson.Gson; + +public class HawkBuilder { + + private static final String STORAGE_TAG_DO_NOT_CHANGE = "Hawk2"; + + private final Context context; + private Storage cryptoStorage; + private Converter converter; + private Parser parser; + private Serializer serializer; + private LogInterceptor logInterceptor; + + public HawkBuilder(Context context) { + HawkUtils.checkNull("Context", context); + + this.context = context.getApplicationContext(); + } + + LogInterceptor getLogInterceptor() { + if (logInterceptor == null) { + logInterceptor = message -> { }; + } + return logInterceptor; + } + + Storage getStorage() { + if (cryptoStorage == null) { + cryptoStorage = new SharedPreferencesStorage(context, STORAGE_TAG_DO_NOT_CHANGE); + } + return cryptoStorage; + } + + Converter getConverter() { + if (converter == null) { + converter = new HawkConverter(getParser()); + } + return converter; + } + + Parser getParser() { + if (parser == null) { + parser = new GsonParser(new Gson()); + } + return parser; + } + + + Serializer getSerializer() { + if (serializer == null) { + serializer = new HawkSerializer(getLogInterceptor()); + } + return serializer; + } + + public void build() { + Hawk.build(this); + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkConverter.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkConverter.java new file mode 100644 index 0000000..0d70364 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkConverter.java @@ -0,0 +1,112 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import com.google.gson.reflect.TypeToken; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +final class HawkConverter implements Converter { + + private final Parser parser; + + public HawkConverter(Parser parser) { + if (parser == null) { + throw new NullPointerException("Parser should not be null"); + } + this.parser = parser; + } + + @Override public String toString(T value) { + if (value == null) { + return null; + } + return parser.toJson(value); + } + + @Override public T fromString(String value, DataInfo info) throws Exception { + if (value == null) { + return null; + } + HawkUtils.checkNull("data info", info); + + Class keyType = info.keyClazz; + Class valueType = info.valueClazz; + + switch (info.dataType) { + case DataInfo.TYPE_OBJECT: + return toObject(value, keyType); + case DataInfo.TYPE_LIST: + return toList(value, keyType); + case DataInfo.TYPE_MAP: + return toMap(value, keyType, valueType); + case DataInfo.TYPE_SET: + return toSet(value, keyType); + default: + return null; + } + } + + private T toObject(String json, Class type) throws Exception { + return parser.fromJson(json, type); + } + + @SuppressWarnings("unchecked") + private T toList(String json, Class type) throws Exception { + if (type == null) { + return (T) new ArrayList<>(); + } + List list = parser.fromJson( + json, + new TypeToken>() { + }.getType() + ); + + int size = list.size(); + for (int i = 0; i < size; i++) { + list.set(i, (T) parser.fromJson(parser.toJson(list.get(i)), type)); + } + return (T) list; + } + + @SuppressWarnings("unchecked") + private T toSet(String json, Class type) throws Exception { + Set resultSet = new HashSet<>(); + if (type == null) { + return (T) resultSet; + } + Set set = parser.fromJson(json, new TypeToken>() { + }.getType()); + + for (T t : set) { + String valueJson = parser.toJson(t); + T value = parser.fromJson(valueJson, type); + resultSet.add(value); + } + return (T) resultSet; + } + + @SuppressWarnings("unchecked") + private T toMap(String json, Class keyType, Class valueType) throws Exception { + Map resultMap = new HashMap<>(); + if (keyType == null || valueType == null) { + return (T) resultMap; + } + Map map = parser.fromJson(json, new TypeToken>() { + }.getType()); + + for (Map.Entry entry : map.entrySet()) { + String keyJson = parser.toJson(entry.getKey()); + K k = parser.fromJson(keyJson, keyType); + + String valueJson = parser.toJson(entry.getValue()); + V v = parser.fromJson(valueJson, valueType); + resultMap.put(k, v); + } + return (T) resultMap; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkFacade.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkFacade.java new file mode 100644 index 0000000..7facfa5 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkFacade.java @@ -0,0 +1,67 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public interface HawkFacade { + + boolean put(String key, T value); + + T get(String key); + + T get(String key, T defaultValue); + + boolean deleteAll(); + + boolean delete(String key); + + boolean contains(String key); + + boolean isBuilt(); + + class EmptyHawkFacade implements HawkFacade { + + @Override + public boolean put(String key, T value) { + throwValidation(); + return false; + } + + @Override + public T get(String key) { + throwValidation(); + return null; + } + + @Override + public T get(String key, T defaultValue) { + throwValidation(); + return null; + } + + @Override + public boolean deleteAll() { + throwValidation(); + return false; + } + + @Override + public boolean delete(String key) { + throwValidation(); + return false; + } + + @Override + public boolean contains(String key) { + throwValidation(); + return false; + } + + @Override + public boolean isBuilt() { + return false; + } + + private void throwValidation() { + throw new IllegalStateException("Hawk is not built. " + + "Please call build() and wait the initialisation finishes."); + } + } +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkSerializer.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkSerializer.java new file mode 100644 index 0000000..b2c9a71 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkSerializer.java @@ -0,0 +1,102 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +class HawkSerializer implements Serializer { + + private static final char DELIMITER = '@'; + private static final String INFO_DELIMITER = "#"; + private static final char NEW_VERSION = 'V'; + + private final LogInterceptor logInterceptor; + + HawkSerializer(LogInterceptor logInterceptor) { + this.logInterceptor = logInterceptor; + } + + @Override + public String serialize(String cipherText, T originalGivenValue) { + HawkUtils.checkNullOrEmpty("Cipher text", cipherText); + HawkUtils.checkNull("Value", originalGivenValue); + + String keyClassName = ""; + String valueClassName = ""; + char dataType; + if (List.class.isAssignableFrom(originalGivenValue.getClass())) { + List list = (List) originalGivenValue; + if (!list.isEmpty()) { + keyClassName = list.get(0).getClass().getName(); + } + dataType = DataInfo.TYPE_LIST; + } else if (Map.class.isAssignableFrom(originalGivenValue.getClass())) { + dataType = DataInfo.TYPE_MAP; + Map map = (Map) originalGivenValue; + if (!map.isEmpty()) { + for (Map.Entry entry : map.entrySet()) { + keyClassName = entry.getKey().getClass().getName(); + valueClassName = entry.getValue().getClass().getName(); + break; + } + } + } else if (Set.class.isAssignableFrom(originalGivenValue.getClass())) { + Set set = (Set) originalGivenValue; + if (!set.isEmpty()) { + Iterator iterator = set.iterator(); + if (iterator.hasNext()) { + keyClassName = iterator.next().getClass().getName(); + } + } + dataType = DataInfo.TYPE_SET; + } else { + dataType = DataInfo.TYPE_OBJECT; + keyClassName = originalGivenValue.getClass().getName(); + } + + return keyClassName + INFO_DELIMITER + + valueClassName + INFO_DELIMITER + + dataType + NEW_VERSION + DELIMITER + + cipherText; + } + + @Override + public DataInfo deserialize(String serializedText) { + String[] infos = serializedText.split(INFO_DELIMITER); + + char type = infos[2].charAt(0); + + Class keyClazz = null; + String firstElement = infos[0]; + if (firstElement != null && firstElement.length() != 0) { + try { + keyClazz = Class.forName(firstElement); + } catch (ClassNotFoundException e) { + logInterceptor.onLog("HawkSerializer -> " + e.getMessage()); + } + } + + Class valueClazz = null; + String secondElement = infos[1]; + if (secondElement != null && secondElement.length() != 0) { + try { + valueClazz = Class.forName(secondElement); + } catch (ClassNotFoundException e) { + logInterceptor.onLog("HawkSerializer -> " + e.getMessage()); + } + } + + String cipherText = getCipherText(infos[infos.length - 1]); + return new DataInfo(type, cipherText, keyClazz, valueClazz); + } + + private String getCipherText(String serializedText) { + int index = serializedText.indexOf(DELIMITER); + if (index == -1) { + throw new IllegalArgumentException("Text should contain delimiter"); + } + return serializedText.substring(index + 1); + } + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkUtils.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkUtils.java new file mode 100644 index 0000000..def213a --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/HawkUtils.java @@ -0,0 +1,23 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +final class HawkUtils { + + private HawkUtils() { } + + public static void checkNull(String message, Object value) { + if (value == null) { + throw new NullPointerException(message + " should not be null"); + } + } + + public static void checkNullOrEmpty(String message, String value) { + if (isEmpty(value)) { + throw new NullPointerException(message + " should not be null or empty"); + } + } + + public static boolean isEmpty(String text) { + return text == null || text.trim().length() == 0; + } + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/LogInterceptor.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/LogInterceptor.java new file mode 100644 index 0000000..43c2fbc --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/LogInterceptor.java @@ -0,0 +1,5 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public interface LogInterceptor { + void onLog(String message); +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Parser.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Parser.java new file mode 100644 index 0000000..ad2eb0c --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Parser.java @@ -0,0 +1,10 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import java.lang.reflect.Type; + +public interface Parser { + + T fromJson(String content, Type type) throws Exception; + String toJson(Object body); + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Serializer.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Serializer.java new file mode 100644 index 0000000..67ba499 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Serializer.java @@ -0,0 +1,8 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public interface Serializer { + + String serialize(String cipherText, T value); + DataInfo deserialize(String plainText); + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/SharedPreferencesStorage.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/SharedPreferencesStorage.java new file mode 100644 index 0000000..291898e --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/SharedPreferencesStorage.java @@ -0,0 +1,45 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +import android.content.Context; +import android.content.SharedPreferences; + +final class SharedPreferencesStorage implements Storage { + + private final SharedPreferences preferences; + + SharedPreferencesStorage(Context context, String tag) { + preferences = context.getSharedPreferences(tag, Context.MODE_PRIVATE); + } + + @Override + public boolean put(String key, T value) { + HawkUtils.checkNull("key", key); + return getEditor().putString(key, String.valueOf(value)).commit(); + } + + @SuppressWarnings("unchecked") + @Override + public T get(String key) { + return (T) preferences.getString(key, null); + } + + @Override + public boolean delete(String key) { + return getEditor().remove(key).commit(); + } + + @Override + public boolean contains(String key) { + return preferences.contains(key); + } + + @Override + public boolean deleteAll() { + return getEditor().clear().commit(); + } + + private SharedPreferences.Editor getEditor() { + return preferences.edit(); + } + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Storage.java b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Storage.java new file mode 100644 index 0000000..8f09e48 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/hawk/Storage.java @@ -0,0 +1,12 @@ +package com.jeluchu.wastickersonline.core.utils.hawk; + +public interface Storage { + + boolean put(String key, T value); + T get(String key); + + boolean delete(String key); + boolean deleteAll(); + boolean contains(String key); + +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/core/utils/room/ListStringConverter.kt b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/room/ListStringConverter.kt new file mode 100644 index 0000000..675a07d --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/core/utils/room/ListStringConverter.kt @@ -0,0 +1,29 @@ +package com.jeluchu.wastickersonline.core.utils.room + +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.jeluchu.wastickersonline.features.sticker.models.StickerEntity +import java.lang.reflect.Type +import java.util.* + +class ListStringConverter { + + private val gson = Gson() + + @TypeConverter + fun stringToListSticker(data: String?): List? { + if (data == null) { + return Collections.emptyList() + } + val listType: Type = object : + TypeToken?>() {}.type + return gson.fromJson>(data, listType) + } + + @TypeConverter + fun listStickerToString(someObjects: List?): String? { + return gson.toJson(someObjects) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerEntity.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerEntity.kt index ac92a48..f1bf3f9 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerEntity.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerEntity.kt @@ -1,6 +1,5 @@ package com.jeluchu.wastickersonline.features.sticker.models - import com.google.gson.annotations.SerializedName data class StickerEntity( diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackEntity.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackEntity.kt index 95ee32e..95ccd8a 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackEntity.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackEntity.kt @@ -1,49 +1,80 @@ package com.jeluchu.wastickersonline.features.sticker.models - +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey import com.google.gson.annotations.SerializedName +@Entity data class StickerPackEntity( + + @PrimaryKey(autoGenerate = true) + val id: Int, + @SerializedName("androidPlayStoreLink") + @ColumnInfo(name = "androidPlayStoreLink") val androidPlayStoreLink: String?, + @SerializedName("iosAppStoreLink") + @ColumnInfo(name = "iosAppStoreLink") val iosAppStoreLink: String?, + @SerializedName("publisherEmail") + @ColumnInfo(name = "publisherEmail") val publisherEmail: String?, + @SerializedName("privacyPolicyWebsite") + @ColumnInfo(name = "privacyPolicyWebsite") val privacyPolicyWebsite: String?, + @SerializedName("licenseAgreementWebsite") + @ColumnInfo(name = "licenseAgreementWebsite") val licenseAgreementWebsite: String?, + @SerializedName("telegram_url") + @ColumnInfo(name = "telegram_url") val telegram_url: String?, + @SerializedName("identifier") + @ColumnInfo(name = "identifier") val identifier: Int?, + @SerializedName("name") + @ColumnInfo(name = "name") val name: String?, + @SerializedName("publisher") + @ColumnInfo(name = "publisher") val publisher: String?, + @SerializedName("publisher_website") + @ColumnInfo(name = "publisher_website") val publisherWebsite: String?, + @SerializedName("stickers") + @ColumnInfo(name = "stickers") val stickers: List?, + @SerializedName("tray_image_file") + @ColumnInfo(name = "tray_image_file") val trayImageFile: String? + ) { - fun toStickersPack(): StickerPack = - StickerPack( - androidPlayStoreLink ?: "", - iosAppStoreLink ?: "", - publisherEmail ?: "", - privacyPolicyWebsite ?: "", - licenseAgreementWebsite ?: "", - telegram_url ?: "", - identifier ?: 0, - name ?: "", - publisher ?: "", - publisherWebsite ?: "", - stickers?.map { it.toStickers() } ?: emptyList(), - trayImageFile ?: "" - ) + fun toStickersPack(): StickerPack = + StickerPack( + androidPlayStoreLink ?: "", + iosAppStoreLink ?: "", + publisherEmail ?: "", + privacyPolicyWebsite ?: "", + licenseAgreementWebsite ?: "", + telegram_url ?: "", + identifier ?: 0, + name ?: "", + publisher ?: "", + publisherWebsite ?: "", + stickers?.map { it.toStickers() } ?: emptyList(), + trayImageFile ?: "" + ) } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackView.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackView.kt index 0a78b95..7e59998 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackView.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/models/StickerPackView.kt @@ -1,7 +1,10 @@ package com.jeluchu.wastickersonline.features.sticker.models +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize import java.io.Serializable +@Parcelize data class StickerPackView( val androidPlayStoreLink: String?, val iosAppStoreLink: String?, @@ -15,4 +18,4 @@ data class StickerPackView( val publisherWebsite: String, val stickers: List, val trayImageFile: String -): Serializable \ No newline at end of file +): Serializable, Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/provider/StickerContentProvider.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/provider/StickerContentProvider.kt index 8926e07..40b6912 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/provider/StickerContentProvider.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/provider/StickerContentProvider.kt @@ -12,12 +12,11 @@ import android.text.TextUtils import android.util.Log import com.jeluchu.wastickersonline.BuildConfig import com.jeluchu.wastickersonline.core.extensions.others.getLastBitFromUrl +import com.jeluchu.wastickersonline.core.utils.hawk.Hawk import com.jeluchu.wastickersonline.features.sticker.models.StickerPackView -import com.orhanobut.hawk.Hawk import java.io.File import java.io.FileNotFoundException import java.io.IOException -import java.util.* class StickerContentProvider : ContentProvider() { @@ -53,7 +52,8 @@ class StickerContentProvider : ContentProvider() { MATCHER.match(uri) val matchCode = MATCHER.match(uri) val pathSegments = uri.pathSegments - Log.d(TAG, """ + Log.d( + TAG, """ openFile: $matchCode$uri ${uri.authority} ${pathSegments[pathSegments.size - 3]}/ @@ -109,14 +109,14 @@ class StickerContentProvider : ContentProvider() { } override fun getType(uri: Uri): String? = - when (MATCHER.match(uri)) { - METADATA_CODE -> "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA - METADATA_CODE_FOR_SINGLE_PACK -> "vnd.android.cursor.item/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA - STICKERS_CODE -> "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + STICKERS - STICKERS_ASSET_CODE -> "image/webp" - STICKER_PACK_TRAY_ICON_CODE -> "image/png" - else -> throw IllegalArgumentException("Unknown URI: $uri") - } + when (MATCHER.match(uri)) { + METADATA_CODE -> "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA + METADATA_CODE_FOR_SINGLE_PACK -> "vnd.android.cursor.item/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA + STICKERS_CODE -> "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + STICKERS + STICKERS_ASSET_CODE -> "image/png" + STICKER_PACK_TRAY_ICON_CODE -> "image/png" + else -> throw IllegalArgumentException("Unknown URI: $uri") + } private fun getStickerPackList(): List = Hawk.get("sticker_packs", ArrayList()) as List @@ -183,11 +183,11 @@ class StickerContentProvider : ContentProvider() { throw UnsupportedOperationException("Not supported") override fun insert(uri: Uri, values: ContentValues?): Uri? = - throw UnsupportedOperationException("Not supported") + throw UnsupportedOperationException("Not supported") override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int = - throw UnsupportedOperationException("Not supported") + throw UnsupportedOperationException("Not supported") private diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersRepository.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersRepository.kt index 455dcdc..870fdc2 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersRepository.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersRepository.kt @@ -1,29 +1,47 @@ package com.jeluchu.wastickersonline.features.sticker.repository +import com.jeluchu.wastickersonline.core.extensions.dateAndTime.isFetchSixHours import com.jeluchu.wastickersonline.core.exception.Failure -import com.jeluchu.wastickersonline.core.functional.Either import com.jeluchu.wastickersonline.core.extensions.request +import com.jeluchu.wastickersonline.core.extensions.sharedprefs.SharedPrefsHelpers +import com.jeluchu.wastickersonline.core.functional.Either import com.jeluchu.wastickersonline.core.platform.NetworkHandler +import com.jeluchu.wastickersonline.core.utils.LocalShared import com.jeluchu.wastickersonline.features.sticker.models.PacksEntity import com.jeluchu.wastickersonline.features.sticker.models.StickerPack import com.jeluchu.wastickersonline.features.sticker.models.StickerPackEntity +import com.jeluchu.wastickersonline.features.sticker.repository.local.StickerLocal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn +import java.util.* interface StickersRepository { fun stickers(): Flow>> class Network(private val networkHandler: NetworkHandler, - private val service: StickersService + private val service: StickersService, + private val local: StickerLocal ) : StickersRepository { + val preferences by lazy { SharedPrefsHelpers() } + override fun stickers(): Flow>> = flow { - emit(getRemoteStickers()) + + val stickers = local.getStickers() + val time = preferences.getLong(LocalShared.Stickers.stickers, 0L) + + if (stickers.isNullOrEmpty() || isFetchSixHours(time)) { + local.deleteAllStickers() + emit(getRemoteStickers()) + } else { + emit(Either.Right(local.getStickers().map { it.toStickersPack() })) + } + }.catch { emit(Either.Left(Failure.CustomError(404, "Not Found"))) }.flowOn(Dispatchers.IO) @@ -34,14 +52,23 @@ interface StickersRepository { true -> request( service.getStickers(), { packsEntity -> + val stickerList: List = packsEntity.stickerPacks + + preferences.saveLong(LocalShared.Stickers.stickers, Date().time) + + addAllStickers(stickerList) stickerList.map { it.toStickersPack() } + }, PacksEntity(emptyList()) ) false -> Either.Left(Failure.NetworkConnection()) } + private fun addAllStickers(stickers: List) { + for (sticker in stickers) { local.addStickers(sticker) } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersService.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersService.kt index 9e5b5f6..907a081 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersService.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/StickersService.kt @@ -1,11 +1,15 @@ package com.jeluchu.wastickersonline.features.sticker.repository +import com.jeluchu.wastickersonline.core.utils.ConstantsMeth import com.jeluchu.wastickersonline.features.sticker.models.PacksEntity import retrofit2.Call import retrofit2.Retrofit -class StickersService(retrofit: Retrofit): StickersApi { +class StickersService(retrofitBuilder: Retrofit.Builder) : StickersApi { + + val retrofit: Retrofit = retrofitBuilder.baseUrl(ConstantsMeth.getApiEndpointStickers()).build() + private val serviceApi by lazy { retrofit.create(StickersApi::class.java) } override fun getStickers(): Call = serviceApi.getStickers() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerDBLocal.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerDBLocal.kt new file mode 100644 index 0000000..fc95738 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerDBLocal.kt @@ -0,0 +1,9 @@ +package com.jeluchu.wastickersonline.features.sticker.repository.local + +import com.jeluchu.wastickersonline.features.sticker.models.StickerPackEntity + +interface StickerDBLocal { + fun getStickers(): List + fun addStickers(stickerPackEntity: StickerPackEntity): Any + fun deleteAllStickers(): Any +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerLocal.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerLocal.kt new file mode 100644 index 0000000..3607674 --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickerLocal.kt @@ -0,0 +1,17 @@ +package com.jeluchu.wastickersonline.features.sticker.repository.local + +import com.jeluchu.wastickersonline.core.database.AppDatabase +import com.jeluchu.wastickersonline.core.extensions.others.ContextHandler +import com.jeluchu.wastickersonline.features.sticker.models.StickerPackEntity + + +class StickerLocal +(contextHandler: ContextHandler): StickerDBLocal { + + private val stickersApi by lazy { AppDatabase.getAppDatabase(contextHandler.appContext).stickerEntityDao() } + + override fun getStickers(): List = stickersApi.getStickers() + override fun addStickers(stickerPackEntity: StickerPackEntity) = stickersApi.insertStickers(stickerPackEntity) + override fun deleteAllStickers() = stickersApi.deleteAll() + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickersDAO.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickersDAO.kt new file mode 100644 index 0000000..2037fea --- /dev/null +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/repository/local/StickersDAO.kt @@ -0,0 +1,21 @@ +package com.jeluchu.wastickersonline.features.sticker.repository.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.jeluchu.wastickersonline.features.sticker.models.StickerPackEntity + +@Dao +interface StickersDAO { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertStickers(newsEntity: StickerPackEntity) + + @Query("DELETE FROM StickerPackEntity") + fun deleteAll() + + @Query("SELECT * FROM StickerPackEntity") + fun getStickers(): List + +} \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/MainActivity.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/MainActivity.kt index fa3c2d2..2283d37 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/MainActivity.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/MainActivity.kt @@ -1,9 +1,7 @@ package com.jeluchu.wastickersonline.features.sticker.view import android.Manifest -import android.content.Intent import android.content.pm.PackageManager -import android.graphics.Bitmap import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat @@ -12,13 +10,16 @@ import com.jeluchu.wastickersonline.R import com.jeluchu.wastickersonline.core.exception.Failure import com.jeluchu.wastickersonline.core.extensions.lifecycle.failure import com.jeluchu.wastickersonline.core.extensions.lifecycle.observe -import com.jeluchu.wastickersonline.features.sticker.adapter.StickersAdapter +import com.jeluchu.wastickersonline.core.extensions.others.exitActivityBottom +import com.jeluchu.wastickersonline.core.extensions.others.openActivity +import com.jeluchu.wastickersonline.core.extensions.others.openActivityRight +import com.jeluchu.wastickersonline.core.extensions.others.statusBarColor +import com.jeluchu.wastickersonline.core.utils.hawk.Hawk import com.jeluchu.wastickersonline.features.sticker.models.StickerPackView +import com.jeluchu.wastickersonline.features.sticker.view.adapter.StickersAdapter import com.jeluchu.wastickersonline.features.sticker.viewmodel.StickersViewModel -import com.orhanobut.hawk.Hawk import kotlinx.android.synthetic.main.activity_main.* import org.koin.android.ext.android.inject -import java.io.* class MainActivity : AppCompatActivity() { @@ -35,37 +36,33 @@ class MainActivity : AppCompatActivity() { } loadStickers() - initListeners() + path = "$filesDir/stickers_asset" permissions setContentView(R.layout.activity_main) + statusBarColor() + initListeners() rvStickersList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) rvStickersList.adapter = adapterStickers - onRefresh() - } private fun initListeners() { adapterStickers.clickListener = { - val intent = Intent(this, StickerDetailsActivity::class.java) - intent.putExtra(EXTRA_STICKERPACK, it) - startActivity(intent) + openActivity(StickerDetailsActivity::class.java) { + putParcelable(EXTRA_STICKERPACK, it) + } + openActivityRight() } } private fun loadStickers() = getStickersView.getStickers() - private fun onRefresh() { srlStickers.setOnRefreshListener { loadStickers() } } private fun renderStickersList(stickersView: List?) { val stickerPack : ArrayList = stickersView as ArrayList - Hawk.put>("sticker_packs", stickerPack) - - srlStickers.isEnabled = true - srlStickers.isRefreshing = false - + Hawk.put("sticker_packs", stickerPack) adapterStickers.collection = stickersView.orEmpty() } private fun handleFailure(failure: Failure?) { failure.toString() } @@ -82,6 +79,11 @@ class MainActivity : AppCompatActivity() { } } + override fun onBackPressed() { + super.onBackPressed() + exitActivityBottom() + } + companion object { const val EXTRA_STICKER_PACK_ID = "sticker_pack_id" const val EXTRA_STICKER_PACK_AUTHORITY = "sticker_pack_authority" @@ -90,37 +92,7 @@ class MainActivity : AppCompatActivity() { @JvmField var path: String? = null - @JvmStatic - fun saveImage(finalBitmap: Bitmap, name: String, identifier: Int) { - val root = "$path/$identifier" - val myDir = File(root) - myDir.mkdirs() - val file = File(myDir, name) - if (file.exists()) file.delete() - try { - val out = FileOutputStream(file) - finalBitmap.compress(Bitmap.CompressFormat.WEBP, 90, out) - out.flush() - out.close() - } catch (e: Exception) { - e.printStackTrace() - } - } - @JvmStatic - fun saveTryImage(finalBitmap: Bitmap, name: String, identifier: String) { - val root = "$path/$identifier" - val myDir = File("$root/try") - myDir.mkdirs() - val fname = name.replace(".png", "").replace(" ", "_") + ".png" - val file = File(myDir, fname) - if (file.exists()) file.delete() - try { - val out = FileOutputStream(file) - finalBitmap.compress(Bitmap.CompressFormat.PNG, 40, out) - out.flush() - out.close() - } catch (e: Exception) { e.printStackTrace() } - } } + } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/StickerDetailsActivity.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/StickerDetailsActivity.kt index 609d76b..c0d1c6b 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/StickerDetailsActivity.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/StickerDetailsActivity.kt @@ -3,39 +3,43 @@ package com.jeluchu.wastickersonline.features.sticker.view import android.content.ActivityNotFoundException import android.content.Intent import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Matrix import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.GridLayoutManager -import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.Target +import androidx.core.graphics.drawable.toBitmap +import androidx.lifecycle.lifecycleScope +import coil.ImageLoader +import coil.load +import coil.request.ImageRequest +import com.jeluchu.wastickersonline.features.sticker.view.adapter.StickersDetailsAdapter import com.jeluchu.wastickersonline.BuildConfig +import com.jeluchu.wastickersonline.R +import com.jeluchu.wastickersonline.core.extensions.others.* +import com.jeluchu.wastickersonline.features.sticker.models.StickerPackView import com.jeluchu.wastickersonline.features.sticker.view.MainActivity.Companion.EXTRA_STICKER_PACK_AUTHORITY import com.jeluchu.wastickersonline.features.sticker.view.MainActivity.Companion.EXTRA_STICKER_PACK_ID import com.jeluchu.wastickersonline.features.sticker.view.MainActivity.Companion.EXTRA_STICKER_PACK_NAME -import com.jeluchu.wastickersonline.R -import com.jeluchu.wastickersonline.core.extensions.others.getLastBitFromUrl -import com.jeluchu.wastickersonline.features.sticker.adapter.StickersDetailsAdapter -import com.jeluchu.wastickersonline.features.sticker.models.StickerPackView import kotlinx.android.synthetic.main.activity_sticker_details.* +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject +import java.io.File +import java.io.FileOutputStream +import java.net.URL class StickerDetailsActivity : AppCompatActivity() { - var stickerPackView: StickerPackView? = null + private var stickerPackView: StickerPackView? = null + private val adapterStickers: StickersDetailsAdapter by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_sticker_details) + statusBarColor() + + ivBack.setOnClickListener { exitActivityLeft() } + if (intent.extras != null) { stickerPackView = intent.getSerializableExtra("stickerpack") as StickerPackView? } @@ -44,69 +48,20 @@ class StickerDetailsActivity : AppCompatActivity() { path = filesDir.toString() + "/" + "stickers_asset" + "/" + stickerPackView!!.identifier + "/" - runOnUiThread { + ivTrayImage.load(stickerPackView!!.trayImageFile) + tvPackName.simpletext(stickerPackView!!.name) + tvAuthor.simpletext(stickerPackView!!.publisher) - val trayImageFile = getLastBitFromUrl(stickerPackView!!.trayImageFile) - - Glide.with(this) - .asBitmap() - .load("https://aruppi.jeluchu.xyz/res/stickers/" + stickerPackView!!.identifier + "/" + trayImageFile) - .addListener(object : RequestListener { - override fun onLoadFailed(e: GlideException?, model: Any, target: Target, isFirstResource: Boolean): Boolean { - return false - } - - override fun onResourceReady(resource: Bitmap, model: Any, target: Target, dataSource: DataSource, isFirstResource: Boolean): Boolean { - val bitmap1 = Bitmap.createBitmap(96, 96, Bitmap.Config.ARGB_8888) - val matrix = Matrix() - val canvas = Canvas(bitmap1) - canvas.drawColor(Color.TRANSPARENT) - matrix.postTranslate( - canvas.width / 2 - resource.width / 2.toFloat(), - canvas.height / 2 - resource.height / 2 - .toFloat()) - canvas.drawBitmap(resource, matrix, null) - MainActivity.saveTryImage(bitmap1, trayImageFile, stickerPackView!!.identifier.toString()) - return true - } - }).submit() - - for (s in stickerPackView!!.stickers) { - - val imageFile = getLastBitFromUrl(s.imageFile) - - Glide.with(this) - .asBitmap() - .apply(RequestOptions().override(512, 512)) - .load("https://aruppi.jeluchu.xyz/res/stickers/" + stickerPackView!!.identifier + "/" + imageFile) - .addListener(object : RequestListener { - override fun onLoadFailed(e: GlideException?, model: Any, target: Target, isFirstResource: Boolean): Boolean { - return false - } - - override fun onResourceReady(resource: Bitmap, model: Any, target: Target, dataSource: DataSource, isFirstResource: Boolean): Boolean { - val bitmap1 = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888) - val matrix = Matrix() - val canvas = Canvas(bitmap1) - canvas.drawColor(Color.TRANSPARENT) - matrix.postTranslate( - canvas.width / 2 - resource.width / 2.toFloat(), - canvas.height / 2 - resource.height / 2 - .toFloat()) - canvas.drawBitmap(resource, matrix, null) - MainActivity.saveImage(bitmap1, imageFile, stickerPackView!!.identifier) - return true - } - }).submit() - } + getStickerPack() + rvStickers.adapter = adapterStickers + rvStickers.apply { + setHasFixedSize(true) + setItemViewCacheSize(30) } + rvStickers.scheduleLayoutAnimation() - val gridLayoutManager = GridLayoutManager(this, 4) - rvStickersPack.layoutManager = gridLayoutManager - rvStickersPack.adapter = adapterStickers - - bAddWhatsApp.setOnClickListener { + mcvAddToWhatsApp.setOnClickListener { val intent = Intent() intent.action = "com.whatsapp.intent.action.ENABLE_STICKER_PACK" intent.putExtra(EXTRA_STICKER_PACK_ID, stickerPackView!!.identifier.toString()) @@ -115,10 +70,76 @@ class StickerDetailsActivity : AppCompatActivity() { try { startActivityForResult(intent, ADD_PACK) } catch (e: ActivityNotFoundException) { - Toast.makeText(this, "No se añadió el paquete de stickers. Si deseas añadirlo, instala o actualiza WhatsApp.", Toast.LENGTH_LONG).show() + Toast.makeText(this@StickerDetailsActivity, "No se añadió el paquete de stickers. Si deseas añadirlo, instala o actualiza WhatsApp.", Toast.LENGTH_LONG).show() } } + mcvAddToTelegram.setOnClickListener { openInCustomTab(stickerPackView!!.publisherWebsite) } + + } + + private fun getStickerPack() { + noCrash { + val trayImageFile = getLastBitFromUrl(stickerPackView!!.trayImageFile) + + val loader = ImageLoader(this@StickerDetailsActivity) + val req = ImageRequest.Builder(this@StickerDetailsActivity) + .data("https://aruppi.jeluchu.xyz/res/stickers/" + stickerPackView!!.identifier + "/" + trayImageFile) + .target { + val myDir = File("${MainActivity.path}/${stickerPackView!!.identifier}/try") + myDir.mkdirs() + val fname = trayImageFile.replace(".png", "").replace(" ", "_") + ".png" + val file = File(myDir, fname) + if (file.exists()) file.delete() + try { + val out = FileOutputStream(file) + it.toBitmap().compress(Bitmap.CompressFormat.PNG, 40, out) + out.close() + } catch (e: Exception) { + e.printStackTrace() + } + }.build() + lifecycleScope.launch { loader.execute(req) } + + for (s in stickerPackView!!.stickers) { + val imageFile = getLastBitFromUrl(s.imageFile) + + val myDir = File("${MainActivity.path}/${stickerPackView!!.identifier}") + myDir.mkdirs() + val file = File(myDir, imageFile) + if (file.exists()) file.delete() + + saveImage( + "https://aruppi.jeluchu.xyz/res/stickers/" + stickerPackView!!.identifier + "/" + imageFile, + File("${MainActivity.path}/${stickerPackView!!.identifier}", imageFile) + ) + } + } + } + + private fun saveImage(imageUrl: String, destinationFile: File) { + try { + Thread { + val url = URL(imageUrl) + val inputStream = url.openStream() + val os = FileOutputStream(destinationFile) + val b = ByteArray(2048) + var length: Int + while (inputStream.read(b).also { length = it } != -1) { + os.write(b, 0, length) + } + inputStream?.close() + os.close() + }.start() + + } catch (e: Exception) { + e.printStackTrace() + } + } + + override fun onBackPressed() { + super.onBackPressed() + exitActivityLeft() } companion object { diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersAdapter.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersAdapter.kt similarity index 96% rename from app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersAdapter.kt rename to app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersAdapter.kt index 559ba46..f5906f5 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersAdapter.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersAdapter.kt @@ -1,12 +1,12 @@ -package com.jeluchu.wastickersonline.features.sticker.adapter +package com.jeluchu.wastickersonline.features.sticker.view.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import coil.api.load +import coil.load +import com.jeluchu.wastickersonline.R import com.jeluchu.wastickersonline.core.extensions.others.inflate import com.jeluchu.wastickersonline.features.sticker.models.StickerPackView -import com.jeluchu.wastickersonline.R import kotlinx.android.synthetic.main.item_sticker.view.* import kotlin.properties.Delegates diff --git a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersDetailsAdapter.kt b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersDetailsAdapter.kt similarity index 85% rename from app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersDetailsAdapter.kt rename to app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersDetailsAdapter.kt index 59e9c21..19e3a4f 100644 --- a/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/adapter/StickersDetailsAdapter.kt +++ b/app/src/main/java/com/jeluchu/wastickersonline/features/sticker/view/adapter/StickersDetailsAdapter.kt @@ -1,13 +1,13 @@ -package com.jeluchu.wastickersonline.features.sticker.adapter +package com.jeluchu.wastickersonline.features.sticker.view.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import coil.api.load +import coil.load import com.jeluchu.wastickersonline.R import com.jeluchu.wastickersonline.core.extensions.others.inflate import com.jeluchu.wastickersonline.features.sticker.models.StickerView -import kotlinx.android.synthetic.main.list_item_image.view.* +import kotlinx.android.synthetic.main.item_stickimage.view.* import kotlin.properties.Delegates class StickersDetailsAdapter : RecyclerView.Adapter(){ @@ -19,7 +19,7 @@ class StickersDetailsAdapter : RecyclerView.Adapter + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/enter_slide_left.xml b/app/src/main/res/anim/enter_slide_left.xml new file mode 100644 index 0000000..fbbed85 --- /dev/null +++ b/app/src/main/res/anim/enter_slide_left.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/enter_slide_right.xml b/app/src/main/res/anim/enter_slide_right.xml new file mode 100644 index 0000000..3244e0a --- /dev/null +++ b/app/src/main/res/anim/enter_slide_right.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_slide_bottom.xml b/app/src/main/res/anim/exit_slide_bottom.xml new file mode 100644 index 0000000..b555c1a --- /dev/null +++ b/app/src/main/res/anim/exit_slide_bottom.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_slide_left.xml b/app/src/main/res/anim/exit_slide_left.xml new file mode 100644 index 0000000..12c6e13 --- /dev/null +++ b/app/src/main/res/anim/exit_slide_left.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_slide_right.xml b/app/src/main/res/anim/exit_slide_right.xml new file mode 100644 index 0000000..454f39b --- /dev/null +++ b/app/src/main/res/anim/exit_slide_right.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/item_animation_from_bottom.xml b/app/src/main/res/anim/item_animation_from_bottom.xml new file mode 100644 index 0000000..b6785c3 --- /dev/null +++ b/app/src/main/res/anim/item_animation_from_bottom.xml @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/item_animation_from_bottom_scale.xml b/app/src/main/res/anim/item_animation_from_bottom_scale.xml new file mode 100644 index 0000000..3fcef00 --- /dev/null +++ b/app/src/main/res/anim/item_animation_from_bottom_scale.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/layout_animation_from_bottom.xml b/app/src/main/res/anim/layout_animation_from_bottom.xml new file mode 100644 index 0000000..f04b269 --- /dev/null +++ b/app/src/main/res/anim/layout_animation_from_bottom.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/layout_animation_from_bottom_scale.xml b/app/src/main/res/anim/layout_animation_from_bottom_scale.xml new file mode 100644 index 0000000..7a0520e --- /dev/null +++ b/app/src/main/res/anim/layout_animation_from_bottom_scale.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_left.xml b/app/src/main/res/drawable/ic_arrow_left.xml new file mode 100644 index 0000000..7960917 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index dd3af6e..03b27f2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,28 +1,40 @@ - + android:background="@color/white" + android:orientation="vertical"> - + - + android:layout_height="wrap_content" + android:paddingStart="@dimen/space_16" + android:paddingTop="@dimen/space_8" + android:paddingEnd="@dimen/space_16" + android:paddingBottom="@dimen/space_8"> - + - \ No newline at end of file + + + + + + diff --git a/app/src/main/res/layout/activity_sticker_details.xml b/app/src/main/res/layout/activity_sticker_details.xml index 8cf3c87..d6058f1 100644 --- a/app/src/main/res/layout/activity_sticker_details.xml +++ b/app/src/main/res/layout/activity_sticker_details.xml @@ -1,37 +1,199 @@ - + android:background="@color/white" + android:orientation="vertical"> - + + + + + + + + + + + + + + + + + + + + + - -