Преглед на файлове

Added progress bar when loading books

sirekanyan преди 8 години
родител
ревизия
28055b63e3

+ 1 - 0
app/build.gradle

@@ -21,6 +21,7 @@ dependencies {
     compile "com.android.support:support-vector-drawable:$support_version"
     compile 'com.android.support.constraint:constraint-layout:1.1.0-beta2'
     compile "io.reactivex.rxjava2:rxjava:2.1.4"
+    compile 'io.reactivex.rxjava2:rxkotlin:2.1.0'
     compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
     compile "com.squareup.retrofit2:retrofit:$retrofit_version"
     compile "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"

+ 2 - 4
app/src/main/java/me/vadik/knigopis/BookActivity.kt

@@ -110,10 +110,8 @@ class BookActivity : AppCompatActivity() {
                   onClick = { position, last ->
                     coverViewPager.currentItem = if (last) 0 else position + 1
                   },
-                  onLoaded = { position ->
-                    if (position == 0) {
-                      coverViewPager.visibility = VISIBLE
-                    }
+                  onFirstLoaded = {
+                    coverViewPager.visibility = VISIBLE
                   })
             }, {
               logError("cannot load thumbnail", it)

+ 6 - 27
app/src/main/java/me/vadik/knigopis/CoverPagerAdapter.kt

@@ -1,23 +1,18 @@
 package me.vadik.knigopis
 
-import android.graphics.drawable.Drawable
 import android.support.v4.view.PagerAdapter
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup.LayoutParams
-import android.view.ViewGroup.LayoutParams.*
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.widget.ImageView
 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
 
 class CoverPagerAdapter(
     private val urls: List<String>,
     private val onClick: (Int, Boolean) -> Unit,
-    private val onLoaded: (Int) -> Unit
+    private val onFirstLoaded: () -> Unit
 ) : PagerAdapter() {
 
   override fun instantiateItem(container: ViewGroup, position: Int): Any {
@@ -28,27 +23,11 @@ class CoverPagerAdapter(
     }
     Glide.with(context)
         .load(urls[position])
-        .listener(object : RequestListener<Drawable> {
-          override fun onResourceReady(
-              resource: Drawable?,
-              model: Any?,
-              target: Target<Drawable>?,
-              dataSource: DataSource?,
-              isFirstResource: Boolean
-          ): Boolean {
-            onLoaded(position)
-            return false
+        .doOnSuccess {
+          if (position == 0) {
+            onFirstLoaded()
           }
-
-          override fun onLoadFailed(
-              e: GlideException?,
-              model: Any?,
-              target: Target<Drawable>?,
-              isFirstResource: Boolean
-          ): Boolean {
-            return false
-          }
-        })
+        }
         .apply(RequestOptions.centerCropTransform())
         .into(imageView)
     container.addView(imageView)

+ 25 - 36
app/src/main/java/me/vadik/knigopis/MainActivity.kt

@@ -12,18 +12,15 @@ import android.support.v7.widget.Toolbar
 import android.view.MenuItem
 import android.view.View
 import android.widget.TextView
-import io.reactivex.Single
+import io.reactivex.rxkotlin.Singles
 import me.vadik.knigopis.adapters.BooksAdapter
 import me.vadik.knigopis.api.BookCoverSearchImpl
 import me.vadik.knigopis.api.Endpoint
 import me.vadik.knigopis.api.ImageEndpoint
 import me.vadik.knigopis.auth.KAuth
 import me.vadik.knigopis.auth.KAuthImpl
-import me.vadik.knigopis.model.Book
-import me.vadik.knigopis.model.BookHeader
-import me.vadik.knigopis.model.CurrentTab
+import me.vadik.knigopis.model.*
 import me.vadik.knigopis.model.CurrentTab.*
-import me.vadik.knigopis.model.FinishedBook
 
 private const val ULOGIN_REQUEST_CODE = 0
 
@@ -32,8 +29,6 @@ class MainActivity : AppCompatActivity() {
   private val api by lazy { app().baseApi.create(Endpoint::class.java) }
   private val auth by lazy { KAuthImpl(applicationContext, api) as KAuth }
   private val allBooks = mutableListOf<Book>()
-  private val finishedBooks = mutableListOf<Book>()
-  private val plannedBooks = mutableListOf<Book>()
   private val booksAdapter by lazy {
     BooksAdapter(BookCoverSearchImpl(
         app().imageApi.create(ImageEndpoint::class.java),
@@ -41,9 +36,9 @@ class MainActivity : AppCompatActivity() {
     ), api, auth)
   }
   private val allBooksAdapter by lazy { booksAdapter.build(allBooks) }
-  private val finishedBooksAdapter by lazy { booksAdapter.build(finishedBooks) }
-  private val plannedBooksAdapter by lazy { booksAdapter.build(plannedBooks) }
   private val fab by lazy { findView<FloatingActionButton>(R.id.add_book_button) }
+  private val progressBar by lazy { findView<View>(R.id.books_progress_bar) }
+  private val booksNotFoundView by lazy { findView<View>(R.id.books_not_found) }
   private lateinit var booksRecyclerView: RecyclerView
   private lateinit var loginOption: MenuItem
   private lateinit var currentTab: CurrentTab
@@ -129,7 +124,7 @@ class MainActivity : AppCompatActivity() {
   }
 
   private fun setCurrentTab(tab: CurrentTab) {
-    if (tab == HOME_TAB) fab.show() else fab.hide()
+    fab.hide()
     currentTab = tab
     when (tab) {
       HOME_TAB -> refreshHomeTab()
@@ -139,49 +134,43 @@ class MainActivity : AppCompatActivity() {
   }
 
   private fun refreshHomeTab() {
+    if (progressBar.alpha > 0) {
+      return
+    }
     booksRecyclerView.adapter = allBooksAdapter
     allBooks.clear()
-    Single.concat(
-        Single.just(listOf(BookHeader("К прочтению"))),
+    Singles.zip(
         api.getPlannedBooks(auth.getAccessToken())
-            .map { it.sortedByDescending { it.priority } },
+            .map { it.sortedByDescending(PlannedBook::priority) },
         api.getFinishedBooks(auth.getAccessToken())
             .map { it.sortedByDescending(FinishedBook::order) }
             .map { it.groupFinishedBooks() }
     ).io2main()
-        .subscribe({
-          allBooks.addAll(it)
+        .doOnSubscribe {
+          progressBar.fadeIn()
+          booksNotFoundView.fadeOut()
+        }
+        .doAfterTerminate {
+          progressBar.fadeOut()
+        }
+        .subscribe({ (planned, finished) ->
+          allBooks.add(BookHeader("К прочтению"))
+          allBooks.addAll(planned)
+          allBooks.addAll(finished)
           allBooksAdapter.notifyDataSetChanged()
+          fab.show()
         }, {
-          toast("Не удалось загрузить книги")
           logError("cannot load books", it)
+          booksNotFoundView.fadeIn()
         })
   }
 
   private fun refreshUsersTab() {
-    booksRecyclerView.adapter = finishedBooksAdapter
-    api.getFinishedBooks(auth.getAccessToken())
-        .io2main()
-        .subscribe({
-          finishedBooks.clear()
-          finishedBooks.addAll(it.sortedByDescending(FinishedBook::order))
-          finishedBooksAdapter.notifyDataSetChanged()
-        }, {
-          logError("cannot load finished books", it)
-        })
+    // todo
   }
 
   private fun refreshNotesTab() {
-    booksRecyclerView.adapter = plannedBooksAdapter
-    api.getPlannedBooks(auth.getAccessToken())
-        .io2main()
-        .subscribe({
-          plannedBooks.clear()
-          plannedBooks.addAll(it.sortedByDescending { it.priority })
-          plannedBooksAdapter.notifyDataSetChanged()
-        }, {
-          logError("cannot load planned books", it)
-        })
+    // todo
   }
 
   private fun List<FinishedBook>.groupFinishedBooks(): List<Book> {

+ 2 - 0
app/src/main/java/me/vadik/knigopis/adapters/BooksAdapter.kt

@@ -76,10 +76,12 @@ class BooksAdapter(
         }
       }
       .bind<ImageView>(R.id.book_image) {
+        hide()
         coverSearch.search(books[it])
             .subscribe({ coverUrl ->
               Glide.with(context)
                   .load(coverUrl)
+                  .doOnSuccess { fadeIn() }
                   .apply(RequestOptions.centerCropTransform())
                   .into(this)
             }, {

+ 40 - 4
app/src/main/java/me/vadik/knigopis/extensions.kt

@@ -10,8 +10,12 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
+import com.bumptech.glide.RequestBuilder
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.Target
 import io.reactivex.Completable
-import io.reactivex.Flowable
 import io.reactivex.Single
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.schedulers.Schedulers
@@ -36,10 +40,42 @@ fun ViewGroup.inflate(@LayoutRes layout: Int): View =
 fun <T> Single<T>.io2main(): Single<T> =
     subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
 
-fun <T> Flowable<T>.io2main(): Flowable<T> =
-    subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
-
 fun Completable.io2main(): Completable =
     subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
 
 fun String.orDefault(default: String) = if (isEmpty()) default else this
+
+fun <T> RequestBuilder<T>.doOnSuccess(onSuccess: () -> Unit): RequestBuilder<T> =
+    listener(object : RequestListener<T> {
+      override fun onResourceReady(
+          resource: T?,
+          model: Any?,
+          target: Target<T>?,
+          dataSource: DataSource?,
+          isFirstResource: Boolean
+      ): Boolean {
+        onSuccess()
+        return false
+      }
+
+      override fun onLoadFailed(
+          e: GlideException?,
+          model: Any?,
+          target: Target<T>?,
+          isFirstResource: Boolean
+      ): Boolean {
+        return false
+      }
+    })
+
+fun View.show() {
+  alpha = 1f
+}
+
+fun View.hide() {
+  alpha = 0f
+}
+
+fun View.fadeIn() = animate().alpha(1f).setDuration(200).start()
+
+fun View.fadeOut() = animate().alpha(0f).setDuration(200).start()

+ 19 - 0
app/src/main/res/layout/activity_main.xml

@@ -32,6 +32,25 @@
             android:layout_height="match_parent"
             tools:listitem="@layout/book" />
 
+        <ProgressBar
+            android:id="@+id/books_progress_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:alpha="0"
+            tools:alpha="1" />
+
+        <TextView
+            android:id="@+id/books_not_found"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:alpha="0"
+            android:gravity="center"
+            android:padding="16dp"
+            android:text="@string/error_loading_books"
+            tools:alpha="1" />
+
         <android.support.design.widget.FloatingActionButton
             android:id="@+id/add_book_button"
             android:layout_width="wrap_content"

+ 2 - 0
app/src/main/res/layout/book.xml

@@ -12,6 +12,8 @@
         android:layout_height="40dp"
         android:layout_gravity="center_vertical"
         android:layout_marginStart="16dp"
+        android:alpha="0"
+        tools:alpha="1"
         tools:ignore="ContentDescription"
         tools:src="@color/colorAccent" />
 

+ 3 - 0
app/src/main/res/values-ru/strings.xml

@@ -41,4 +41,7 @@
     <string name="about_developer">Разработка: vadik@sirekanyan.com</string>
     <string name="about_designer">Дизайн: anna@sirekanyan.com</string>
 
+    <!-- error -->
+    <string name="error_loading_books">Проверьте подключение к сети.</string>
+
 </resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -41,4 +41,7 @@
     <string name="about_developer">Developer: vadik@sirekanyan.com</string>
     <string name="about_designer">Designer: anna@sirekanyan.com</string>
 
+    <!-- error -->
+    <string name="error_loading_books">Check your network connection.</string>
+
 </resources>