diff --git a/GifsKeyStore b/GifsKeyStore new file mode 100644 index 0000000..1f72c1c Binary files /dev/null and b/GifsKeyStore differ diff --git a/app/build.gradle b/app/build.gradle index 0c9ab02..c8d349b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,14 +9,37 @@ android { applicationId "com.haretskiy.pavel.gifrandom" minSdkVersion 21 targetSdkVersion 27 - versionCode 1 - versionName "1.0" + versionCode 3 + versionName "1.02" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + signingConfigs { + debug { + keyAlias 'Debug' + keyPassword 'qazwsxedc' + storeFile file('../GifsKeyStore') + storePassword 'qazwsxedc' + } + release { + keyAlias 'Release' + keyPassword 'qazwsxedc' + storeFile file('../GifsKeyStore') + storePassword 'qazwsxedc' + } + } buildTypes { + debug { + minifyEnabled false + shrinkResources false + debuggable = true + signingConfig signingConfigs.debug + } release { + debuggable = false minifyEnabled true + shrinkResources true + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -27,42 +50,42 @@ android { def retrofitVersion = '2.3.0' def support_version = "27.1.1" -def glideVersion = '4.7.1' -def koin_version = '0.9.2' +def glideVersion = '4.8.0' +def koin_version = '1.0.2' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + // Support implementation "com.android.support:appcompat-v7:${support_version}" implementation "com.android.support:design:${support_version}" implementation "com.android.support:recyclerview-v7:${support_version}" implementation "com.android.support:cardview-v7:${support_version}" implementation "com.android.support:support-v4:${support_version}" + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + // Retrofit2 implementation "com.squareup.retrofit2:retrofit:${retrofitVersion}" implementation "com.squareup.retrofit2:converter-gson:${retrofitVersion}" implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofitVersion}" - implementation "io.reactivex.rxjava2:rxandroid:2.0.2" - implementation "io.reactivex.rxjava2:rxjava:2.1.10" + // RxJava2 + implementation "io.reactivex.rxjava2:rxandroid:2.1.0" + implementation "io.reactivex.rxjava2:rxjava:2.2.5" + // Glide implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.10" implementation "com.github.bumptech.glide:glide:$glideVersion" - implementation 'com.android.support.constraint:constraint-layout:1.1.0' kapt "com.github.bumptech.glide:compiler:$glideVersion" - // Koin for Kotlin - implementation "org.koin:koin-core:$koin_version" // Koin for Android implementation "org.koin:koin-android:$koin_version" - // Koin for Android Architecture Components - implementation "org.koin:koin-android-architecture:$koin_version" + // Koin Android Scope feature + implementation "org.koin:koin-android-scope:$koin_version" + // Koin Android ViewModel feature + implementation "org.koin:koin-android-viewmodel:$koin_version" // Paging - implementation "android.arch.paging:runtime:1.0.0" - - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation "android.arch.paging:runtime:1.0.1" } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f1b4245..30e1c20 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -19,3 +19,49 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + + +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + +# Uncomment for DexGuard only +#-keepresourcexmlelements manifest/application/meta-data@value=GlideModule + +-dontwarn retrofit2.** +-dontwarn org.codehaus.mojo.** +-keep class retrofit2.** { *; } +-keepattributes Exceptions +-keepattributes RuntimeVisibleAnnotations +-keepattributes RuntimeInvisibleAnnotations +-keepattributes RuntimeVisibleParameterAnnotations +-keepattributes RuntimeInvisibleParameterAnnotations + +-keepattributes EnclosingMethod +-keepclasseswithmembers class * { + @retrofit2.http.* ; +} +-keepclasseswithmembers interface * { + @retrofit2.* ; +} +# Platform calls Class.forName on types which do not exist on Android to determine platform. +-dontnote retrofit2.Platform +# Platform used when running on RoboVM on iOS. Will not be used at runtime. +-dontnote retrofit2.Platform$IOS$MainThreadExecutor +# Platform used when running on Java 8 VMs. Will not be used at runtime. +-dontwarn retrofit2.Platform$Java8 +# Retain generic type information for use by reflection by converters and adapters. +-keepattributes Signature +# Retain declared checked exceptions for use by a Proxy instance. +-keepattributes Exceptions + + +-keepattributes Signature +-keepattributes Annotation +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** +-dontwarn okio.** \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0ad20e1..5d08a75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,9 +19,7 @@ - + \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/Constants.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/Constants.kt index 544dd8d..df5965b 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/Constants.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/Constants.kt @@ -7,6 +7,7 @@ const val RESPONSE = "RESPONSE" const val EMPTY_STRING = "" const val DEFAULT_RATING = "Y" +const val ZERO = 0 const val BASE_URL = "https://api.giphy.com/v1/" const val API_KEY = "ixOZB0aOMOF7Ivx1vuTIBAeXdksNdGTB" @@ -16,7 +17,8 @@ const val PREFETCH_SIZE = 10 const val ZERO_OFFSET = "0" const val BUNDLE_KEY_URL_DETAIL = "BUNDLE_KEY_URL_DETAIL" -const val BUNDLE_KEY_URL = "BUNDLE_KEY_URL" const val VIEW_NAME_IMAGE = "VIEW_NAME_IMAGE" -const val START_ANIMATION_DELAY = 600L \ No newline at end of file +const val START_ANIMATION_DELAY = 600L + +const val SHARE_TYPE_TEXT = "text/plain" \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/DetailActivity.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/DetailActivity.kt index e6abc78..86d2778 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/DetailActivity.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/DetailActivity.kt @@ -8,13 +8,14 @@ import android.support.v7.app.AppCompatActivity import com.haretskiy.pavel.gifrandom.* import com.haretskiy.pavel.gifrandom.databinding.ActivityDetailBinding import com.haretskiy.pavel.gifrandom.viewModels.DetailViewModel -import org.koin.android.architecture.ext.viewModel +import org.koin.android.viewmodel.ext.android.viewModel +import org.koin.core.parameter.ParameterList class DetailActivity : AppCompatActivity() { private var url = EMPTY_STRING - private val detailViewModel: DetailViewModel by viewModel { mapOf(BUNDLE_KEY_URL to url) } + private val detailViewModel: DetailViewModel by viewModel { ParameterList(url) } private val binding: ActivityDetailBinding by lazy { DataBindingUtil.setContentView(this, R.layout.activity_detail) as ActivityDetailBinding diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/MainActivity.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/MainActivity.kt index b7817f7..96566b9 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/MainActivity.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/activities/MainActivity.kt @@ -6,13 +6,14 @@ import android.databinding.DataBindingUtil import android.os.Bundle import android.support.v7.app.AppCompatActivity import com.haretskiy.pavel.gifrandom.R +import com.haretskiy.pavel.gifrandom.ZERO import com.haretskiy.pavel.gifrandom.adapters.GifAdapter import com.haretskiy.pavel.gifrandom.databinding.ActivityMainBinding import com.haretskiy.pavel.gifrandom.viewModels.MainViewModel -import kotlinx.android.synthetic.main.activity_main.* -import org.koin.android.architecture.ext.viewModel +import kotlinx.android.synthetic.main.main_content.* +import kotlinx.android.synthetic.main.toolbar.* import org.koin.android.ext.android.inject - +import org.koin.android.viewmodel.ext.android.viewModel class MainActivity : AppCompatActivity() { @@ -27,18 +28,27 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mainViewModel.initPaging() binding.model = mainViewModel + mainViewModel.setBinding(binding) initRecyclerView() + initToolbar() } private fun initRecyclerView() { - mainViewModel.pagedListLiveData.observe(this, Observer> { urls -> adapter.submitList(urls) }) + mainViewModel.positLiveData.observe(this, Observer { + if (it == true) { + rv_gifs.scrollToPosition(ZERO) + } + }) rv_gifs.adapter = adapter } + private fun initToolbar() { + setSupportActionBar(toolbar) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/data/RepositoryImpl.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/data/RepositoryImpl.kt index d755e84..827b19c 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/data/RepositoryImpl.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/data/RepositoryImpl.kt @@ -11,37 +11,32 @@ class RepositoryImpl( private val restApi: RestApiImpl) : Repository { override fun loadTrendingGifs(rating: String, offset: String, resultCallback: ResultCallback) { - transformToListString(restApi.loadGifs(rating, offset)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - if (it != null) { - resultCallback.onResult(it) - } - }) + load(restApi.loadGifs(rating, offset), resultCallback) } override fun loadGifsByWord(word: String, rating: String, offset: String, resultCallback: ResultCallback) { - transformToListString(restApi.loadGifsByWord(word, rating, offset)) - .subscribeOn(Schedulers.io()) + load(restApi.loadGifsByWord(word, rating, offset), resultCallback) + } + + private fun load(obs: Observable, resultCallback: ResultCallback) { + obs.subscribeOn(Schedulers.io()) + .map { + it.data.map { + it.images?.original?.url ?: EMPTY_STRING + } + } .observeOn(AndroidSchedulers.mainThread()) .subscribe( { if (it != null) { resultCallback.onResult(it) } + }, + { + resultCallback.onResult(emptyList()) }) } - private fun transformToListString(response: Observable): Observable> = - response.map { - val list = it.data.map { - it.images?.original?.url ?: EMPTY_STRING - } - list - } - interface ResultCallback { fun onResult(list: List) } diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/di/Modules.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/di/Modules.kt index e6127ff..4ffe446 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/di/Modules.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/di/Modules.kt @@ -2,11 +2,10 @@ package com.haretskiy.pavel.gifrandom.di import com.google.gson.GsonBuilder import com.haretskiy.pavel.gifrandom.BASE_URL -import com.haretskiy.pavel.gifrandom.BUNDLE_KEY_URL import com.haretskiy.pavel.gifrandom.adapters.GifAdapter import com.haretskiy.pavel.gifrandom.data.Repository import com.haretskiy.pavel.gifrandom.data.RepositoryImpl -import com.haretskiy.pavel.gifrandom.rest.JsonLoggingInterceptor +import com.haretskiy.pavel.gifrandom.rest.JsonInterceptor import com.haretskiy.pavel.gifrandom.rest.RestApi import com.haretskiy.pavel.gifrandom.rest.RestApiImpl import com.haretskiy.pavel.gifrandom.utils.* @@ -15,20 +14,25 @@ import com.haretskiy.pavel.gifrandom.utils.pagging.GifsSourceFactory import com.haretskiy.pavel.gifrandom.viewModels.DetailViewModel import com.haretskiy.pavel.gifrandom.viewModels.MainViewModel import okhttp3.OkHttpClient -import org.koin.android.architecture.ext.viewModel import org.koin.android.ext.koin.androidApplication -import org.koin.dsl.context.ParameterProvider +import org.koin.android.viewmodel.ext.koin.viewModel import org.koin.dsl.module.Module -import org.koin.dsl.module.applicationContext +import org.koin.dsl.module.module import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory - -val restModule: Module = applicationContext { - bean { OkHttpClient.Builder().addInterceptor(JsonLoggingInterceptor()).build() } - bean { GsonBuilder().setLenient().create() } - bean { +val restModule: Module = module(definition = { + single { + OkHttpClient.Builder() + .addInterceptor(JsonInterceptor()) + .build() + } + single { + GsonBuilder().setLenient() + .create() + } + single { Retrofit.Builder() .baseUrl(BASE_URL) .client(get()) @@ -37,27 +41,25 @@ val restModule: Module = applicationContext { .build() .create(RestApi::class.java) } - bean { RestApiImpl(get()) } -} + single { RestApiImpl(get()) } +}) -val appModule: Module = applicationContext { - bean { Toaster(androidApplication()) } - bean { ImageLoaderImpl() as ImageLoader } - bean { RouterImpl(androidApplication()) as Router } - bean { RepositoryImpl(get()) as Repository } - bean { DiffCallBack() } - bean { GifsSourceFactory(get()) } +val appModule: Module = module(definition = { + single { Toaster(androidApplication()) } + single { ImageLoaderImpl() as ImageLoader } + single { RouterImpl(androidApplication()) as Router } + single { RepositoryImpl(get()) as Repository } + single { DiffCallBack() } + single { GifsSourceFactory(get()) } factory { GifAdapter(get(), get(), get()) } -} +}) -val viewModelModel: Module = applicationContext { +val viewModelModule: Module = module(definition = { viewModel { MainViewModel(androidApplication(), get()) } - viewModel { params: ParameterProvider -> - DetailViewModel(get(), params[BUNDLE_KEY_URL]) + viewModel {parameterList -> + DetailViewModel(get(), parameterList[0]) } -} - - -val modules = listOf(restModule, appModule, viewModelModel) +}) +val modules = listOf(restModule, appModule, viewModelModule) diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonLoggingInterceptor.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonInterceptor.kt similarity index 80% rename from app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonLoggingInterceptor.kt rename to app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonInterceptor.kt index 66be238..5df51a0 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonLoggingInterceptor.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/JsonInterceptor.kt @@ -6,16 +6,21 @@ import okhttp3.Interceptor import okhttp3.Response import okhttp3.ResponseBody -class JsonLoggingInterceptor : Interceptor { + +class JsonInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response? { val request = chain.request() try { val t1 = System.nanoTime() - Log.d(START, "Sending request ${request.url()} Headers: ${request.headers()}") + val url = request.url().newBuilder().addQueryParameter("apikey", API_KEY).build() + val requestBuilder = request.newBuilder().url(url) + val newRequest = requestBuilder.build() + + Log.d(START, "Sending request ${newRequest.url()} Headers: ${newRequest.headers()}") - val response = chain.proceed(request) + val response = chain.proceed(newRequest) val responseBodyString = response.body()?.string() ?: EMPTY_STRING diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApi.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApi.kt index f70453b..9baaf7d 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApi.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApi.kt @@ -10,14 +10,12 @@ import retrofit2.http.Query interface RestApi { @GET("gifs/trending") - fun loadGifs(@Query("api_key") aipKey: String, - @Query("limit") limit: Int, + fun loadGifs(@Query("limit") limit: Int, @Query("rating") rating: String, @Query("offset") offset: String = ZERO_OFFSET): Observable @GET("gifs/search") - fun loadGifsBySearchWord(@Query("api_key") apiKey: String, - @Query("q") searchWord: String, + fun loadGifsBySearchWord(@Query("q") searchWord: String, @Query("limit") limit: Int, @Query("rating") rating: String, @Query("offset") offset: String = ZERO_OFFSET): Observable diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApiImpl.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApiImpl.kt index 5cacf71..e635f34 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApiImpl.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/rest/RestApiImpl.kt @@ -1,13 +1,12 @@ package com.haretskiy.pavel.gifrandom.rest -import com.haretskiy.pavel.gifrandom.API_KEY import com.haretskiy.pavel.gifrandom.LIMIT import com.haretskiy.pavel.gifrandom.ZERO_OFFSET class RestApiImpl(private val restApi: RestApi) { - fun loadGifs(rating: String, offset: String = ZERO_OFFSET) = restApi.loadGifs(API_KEY, LIMIT, rating, offset) + fun loadGifs(rating: String, offset: String = ZERO_OFFSET) = restApi.loadGifs(LIMIT, rating, offset) - fun loadGifsByWord(word: String, rating: String, offset: String = ZERO_OFFSET) = restApi.loadGifsBySearchWord(API_KEY, word, LIMIT, rating, offset) + fun loadGifsByWord(word: String, rating: String, offset: String = ZERO_OFFSET) = restApi.loadGifsBySearchWord(word, LIMIT, rating, offset) } \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Router.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Router.kt index 3bc41be..d4376d0 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Router.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Router.kt @@ -8,4 +8,5 @@ interface Router { fun startDetailActivity(url: String) fun startDetailActivity(context: Context, imageView: View, url: String) + fun shareGif(urlStr: String) } \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/RouterImpl.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/RouterImpl.kt index d02928b..3020eaa 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/RouterImpl.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/RouterImpl.kt @@ -7,6 +7,7 @@ import android.support.v4.app.ActivityOptionsCompat import android.support.v4.util.Pair import android.view.View import com.haretskiy.pavel.gifrandom.BUNDLE_KEY_URL_DETAIL +import com.haretskiy.pavel.gifrandom.SHARE_TYPE_TEXT import com.haretskiy.pavel.gifrandom.VIEW_NAME_IMAGE import com.haretskiy.pavel.gifrandom.activities.DetailActivity @@ -32,4 +33,12 @@ class RouterImpl(private var context: Context) : Router { context.startActivity(intent, activityOptions.toBundle()) } + override fun shareGif(urlStr: String) { + val shareIntent = Intent(Intent.ACTION_SEND) + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + shareIntent.type = SHARE_TYPE_TEXT + shareIntent.putExtra(Intent.EXTRA_TEXT, urlStr) + + context.startActivity(shareIntent) + } } \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Toaster.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Toaster.kt index 9cb8e93..9d44c87 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Toaster.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/Toaster.kt @@ -5,7 +5,7 @@ import android.widget.Toast class Toaster(private val context: Context) { - fun showToast(message: String, isLong: Boolean) { + fun showToast(message: String, isLong: Boolean = false) { Toast.makeText(context, message, if (isLong) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show() } } \ No newline at end of file diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/pagging/GifsDataSource.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/pagging/GifsDataSource.kt index 174baf9..8bfabcf 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/pagging/GifsDataSource.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/utils/pagging/GifsDataSource.kt @@ -3,49 +3,47 @@ package com.haretskiy.pavel.gifrandom.utils.pagging import android.arch.paging.PositionalDataSource import com.haretskiy.pavel.gifrandom.ZERO_OFFSET import com.haretskiy.pavel.gifrandom.data.Repository -import com.haretskiy.pavel.gifrandom.data.RepositoryImpl +import com.haretskiy.pavel.gifrandom.data.RepositoryImpl.ResultCallback class GifsDataSource( private val repository: Repository, - var gifsLoadedCallback: GifsLoadedCallback, + private var gifsLoadedCallback: GifsLoadedCallback, private var rating: String, private var word: String) : PositionalDataSource() { override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { gifsLoadedCallback.onStartPageLoad() - if (word.isEmpty()) { - repository.loadTrendingGifs(rating, params.startPosition.toString(), object : RepositoryImpl.ResultCallback { - override fun onResult(list: List) { - callback.onResult(list) - gifsLoadedCallback.onFinishPageLoad() - } - }) - } else { - repository.loadGifsByWord(word, rating, params.startPosition.toString(), object : RepositoryImpl.ResultCallback { - override fun onResult(list: List) { - callback.onResult(list) - gifsLoadedCallback.onFinishPageLoad() - } - }) + when { + word.isEmpty() -> repository.loadTrendingGifs(rating, params.startPosition.toString(), resultCallback(callback)) + else -> repository.loadGifsByWord(word, rating, params.startPosition.toString(), resultCallback(callback)) } } override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) { gifsLoadedCallback.onStartInitialLoad() - if (word.isEmpty()) { - repository.loadTrendingGifs(rating, ZERO_OFFSET, object : RepositoryImpl.ResultCallback { - override fun onResult(list: List) { - callback.onResult(list, params.requestedStartPosition) - gifsLoadedCallback.onFinishInitialLoad() - } - }) - } else { - repository.loadGifsByWord(word, rating, ZERO_OFFSET, object : RepositoryImpl.ResultCallback { - override fun onResult(list: List) { - callback.onResult(list, params.requestedStartPosition) - gifsLoadedCallback.onFinishInitialLoad() - } - }) + when { + word.isEmpty() -> repository.loadTrendingGifs(rating, ZERO_OFFSET, initialResultCallback(params, callback)) + else -> repository.loadGifsByWord(word, rating, ZERO_OFFSET, initialResultCallback(params, callback)) + } + } + + private fun initialResultCallback(params: LoadInitialParams, callback: LoadInitialCallback) = object : ResultCallback { + override fun onResult(list: List) = try { + var startPos = params.requestedStartPosition + when { + startPos < 0 -> startPos = 0 + } + callback.onResult(list, startPos) + gifsLoadedCallback.onFinishInitialLoad() + } catch (ex: Exception) { + ex.printStackTrace() + } + } + + private fun resultCallback(callback: LoadRangeCallback) = object : ResultCallback { + override fun onResult(list: List) { + callback.onResult(list) + gifsLoadedCallback.onFinishPageLoad() } } diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/GifHolderViewModel.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/GifHolderViewModel.kt index 01a6a7b..d89c707 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/GifHolderViewModel.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/GifHolderViewModel.kt @@ -29,6 +29,10 @@ class GifHolderViewModel( router.startDetailActivity(mItemGifBinding.root.context, mItemGifBinding.imageView, urlStr) } + fun onClickShare(@Suppress("UNUSED_PARAMETER") v: View) { + router.shareGif(urlStr) + } + fun initObservers(lifecycleOwner: LifecycleOwner) { progressController.subscribeOnProgressChanges(lifecycleOwner, Observer { progress.set(if (it == true) View.VISIBLE else View.GONE) diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/MainViewModel.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/MainViewModel.kt index b6e25c1..b34e47b 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/MainViewModel.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/viewModels/MainViewModel.kt @@ -2,7 +2,7 @@ package com.haretskiy.pavel.gifrandom.viewModels import android.app.Application import android.arch.lifecycle.AndroidViewModel -import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData import android.arch.paging.LivePagedListBuilder import android.arch.paging.PagedList import android.databinding.ObservableField @@ -10,6 +10,7 @@ import android.databinding.ObservableInt import android.view.View import android.widget.AdapterView import com.haretskiy.pavel.gifrandom.* +import com.haretskiy.pavel.gifrandom.databinding.ActivityMainBinding import com.haretskiy.pavel.gifrandom.utils.pagging.GifsDataSource import com.haretskiy.pavel.gifrandom.utils.pagging.GifsSourceFactory import java.util.concurrent.Executors @@ -18,34 +19,32 @@ import java.util.concurrent.Executors class MainViewModel(private val context: Application, private val factory: GifsSourceFactory) : AndroidViewModel(context) { - private lateinit var config: PagedList.Config - lateinit var pagedListLiveData: LiveData> + private val config = PagedList.Config.Builder() + .setEnablePlaceholders(false) + .setInitialLoadSizeHint(INITIAL_LOAD_SIZE) + .setPrefetchDistance(PREFETCH_SIZE) + .setPageSize(LIMIT) + .build() + + val pagedListLiveData = LivePagedListBuilder( + factory.initCallback(object : GifsDataSource.GifsLoadedCallback { + override fun onStartInitialLoad() { + progress.set(View.VISIBLE) + } + + override fun onFinishInitialLoad() { + progress.set(View.GONE) + } + }), config) + .setFetchExecutor(Executors.newSingleThreadExecutor()) + .build() val searchWord: ObservableField = ObservableField() - val ratingSelectedPos = ObservableInt(0) + val ratingSelectedPos = ObservableInt(ZERO) val progress = ObservableInt(View.VISIBLE) + val toolbarVisibility = ObservableInt(View.VISIBLE) - fun initPaging() { - config = PagedList.Config.Builder() - .setEnablePlaceholders(false) - .setInitialLoadSizeHint(INITIAL_LOAD_SIZE) - .setPrefetchDistance(PREFETCH_SIZE) - .setPageSize(LIMIT) - .build() - - pagedListLiveData = LivePagedListBuilder( - factory.initCallback(object : GifsDataSource.GifsLoadedCallback { - override fun onStartInitialLoad() { - progress.set(View.VISIBLE) - } - - override fun onFinishInitialLoad() { - progress.set(View.GONE) - } - }), config) - .setFetchExecutor(Executors.newSingleThreadExecutor()) - .build() - } + val positLiveData = MutableLiveData() private fun getCurrentRating(): String { val ratings = context.resources.getStringArray(R.array.ratings) @@ -58,9 +57,26 @@ class MainViewModel(private val context: Application, factory.invalidate() } + fun onClickFilter(@Suppress("UNUSED_PARAMETER") v: View) { + positLiveData.postValue(true) + } + + @Suppress("UNUSED_PARAMETER") fun onRatingSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { } + fun setBinding(binding: ActivityMainBinding) { + binding.appbar.addOnOffsetChangedListener { appBarLayout, verticalOffset -> + if (Math.abs(verticalOffset) - appBarLayout.totalScrollRange >= 0) { + // Collapse + toolbarVisibility.set(View.VISIBLE) + } else { + //Expanded + toolbarVisibility.set(View.GONE) + } + } + } + } diff --git a/app/src/main/java/com/haretskiy/pavel/gifrandom/views/GifHolder.kt b/app/src/main/java/com/haretskiy/pavel/gifrandom/views/GifHolder.kt index d293fff..bad4395 100644 --- a/app/src/main/java/com/haretskiy/pavel/gifrandom/views/GifHolder.kt +++ b/app/src/main/java/com/haretskiy/pavel/gifrandom/views/GifHolder.kt @@ -23,5 +23,4 @@ class GifHolder(private val imageLoader: ImageLoader, mItemGifBinding.executePendingBindings() } - } \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index c7bd21d..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index d5fccc5..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_share.png b/app/src/main/res/drawable/ic_share.png new file mode 100755 index 0000000..7e8ddc8 Binary files /dev/null and b/app/src/main/res/drawable/ic_share.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c243f9f..d05706d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -9,63 +9,48 @@ type="com.haretskiy.pavel.gifrandom.viewModels.MainViewModel" /> - - + android:layout_height="wrap_content"> - + android:background="@android:color/white" + android:theme="@style/AppTheme" + app:layout_scrollFlags="scroll|exitUntilCollapsed"> - + -