فهرست منبع

Added initial implementation of Following and Notes tabs

sirekanyan 7 سال پیش
والد
کامیت
19b7b311cd

+ 64 - 7
app/src/main/java/me/vadik/knigopis/MainActivity.kt

@@ -18,6 +18,8 @@ import io.reactivex.rxkotlin.Singles
 import kotlinx.android.synthetic.main.about.view.*
 import kotlinx.android.synthetic.main.activity_main.*
 import me.vadik.knigopis.adapters.BooksAdapter
+import me.vadik.knigopis.adapters.notes.NotesAdapter
+import me.vadik.knigopis.adapters.users.UsersAdapter
 import me.vadik.knigopis.api.BookCoverSearchImpl
 import me.vadik.knigopis.api.Endpoint
 import me.vadik.knigopis.api.ImageEndpoint
@@ -25,6 +27,8 @@ import me.vadik.knigopis.auth.KAuth
 import me.vadik.knigopis.auth.KAuthImpl
 import me.vadik.knigopis.model.*
 import me.vadik.knigopis.model.CurrentTab.*
+import me.vadik.knigopis.model.note.Note
+import me.vadik.knigopis.model.subscription.Subscription
 import retrofit2.HttpException
 
 private const val ULOGIN_REQUEST_CODE = 0
@@ -38,6 +42,8 @@ class MainActivity : AppCompatActivity(), Router {
     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 allUsers = mutableListOf<Subscription>()
+    private val allNotes = mutableListOf<Note>()
     private val booksAdapter by lazy {
         BooksAdapter(
             BookCoverSearchImpl(
@@ -47,6 +53,8 @@ class MainActivity : AppCompatActivity(), Router {
         )
     }
     private val allBooksAdapter by lazy { booksAdapter.build(allBooks) }
+    private val usersAdapter by lazy { UsersAdapter(allUsers) }
+    private val notesAdapter by lazy { NotesAdapter(allNotes) }
     private val navigation by lazy {
         findView<BottomNavigationView>(R.id.navigation).apply {
             visibility = if (config.isDevMode()) View.VISIBLE else View.GONE
@@ -64,6 +72,8 @@ class MainActivity : AppCompatActivity(), Router {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
         initRecyclerView(booksRecyclerView)
+        initRecyclerView(usersRecyclerView)
+        initRecyclerView(notesRecyclerView)
         initNavigationView()
         initToolbar(toolbar)
         addBookButton.setOnClickListener {
@@ -159,10 +169,6 @@ class MainActivity : AppCompatActivity(), Router {
                     AlertDialog.Builder(this).setView(dialogView).show()
                     true
                 }
-                R.id.option_refresh -> {
-                    refresh()
-                    true
-                }
                 else -> false
             }
         }
@@ -241,6 +247,9 @@ class MainActivity : AppCompatActivity(), Router {
     }
 
     private fun refreshHomeTab() {
+        usersRecyclerView.hideNow()
+        notesRecyclerView.hideNow()
+        booksRecyclerView.showNow()
         if (booksProgressBar.alpha > 0) {
             return
         }
@@ -265,7 +274,7 @@ class MainActivity : AppCompatActivity(), Router {
                 booksProgressBar.show()
                 booksPlaceholder.hide()
             }
-            .doAfterTerminate {
+            .doFinally {
                 booksProgressBar.hide()
             }
             .subscribe({ books ->
@@ -290,11 +299,59 @@ class MainActivity : AppCompatActivity(), Router {
     }
 
     private fun refreshUsersTab() {
-        // todo
+        booksRecyclerView.hideNow()
+        notesRecyclerView.hideNow()
+        usersRecyclerView.showNow()
+        usersRecyclerView.adapter = usersAdapter
+        allUsers.clear()
+        api.getSubscriptions(auth.getAccessToken())
+            .io2main()
+            .doOnSubscribe {
+                booksProgressBar.show()
+                booksPlaceholder.hide()
+            }
+            .doFinally {
+                booksProgressBar.hide()
+            }
+            .subscribe({ subscriptions ->
+                allUsers.addAll(subscriptions)
+                usersAdapter.notifyDataSetChanged()
+            }, {
+                logError("cannot load users", it)
+                booksPlaceholder.setText(
+                    if (it is HttpException && it.code() == 401) {
+                        R.string.error_unauthorized
+                    } else {
+                        R.string.error_loading_books
+                    }
+                )
+                booksPlaceholder.show()
+            })
     }
 
     private fun refreshNotesTab() {
-        // todo
+        booksRecyclerView.hideNow()
+        usersRecyclerView.hideNow()
+        notesRecyclerView.showNow()
+        notesRecyclerView.adapter = notesAdapter
+        allNotes.clear()
+        api.getLatestBooksWithNotes()
+            .io2main()
+            .doOnSubscribe {
+                booksProgressBar.show()
+                booksPlaceholder.hide()
+            }
+            .doFinally {
+                booksProgressBar.hide()
+            }
+            .subscribe({ notes ->
+                allNotes.addAll(notes.values)
+                notesAdapter.notifyDataSetChanged()
+            }, {
+                logError("cannot load notes", it)
+                booksPlaceholder.setText(R.string.error_loading_books)
+                booksPlaceholder.show()
+            })
     }
 
     private fun List<FinishedBook>.groupFinishedBooks(): List<Book> {

+ 6 - 3
app/src/main/java/me/vadik/knigopis/adapters/BooksAdapter.kt

@@ -6,6 +6,7 @@ import android.widget.ImageView
 import android.widget.ProgressBar
 import android.widget.TextView
 import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
 import com.bumptech.glide.request.RequestOptions
 import me.vadik.knigopis.*
 import me.vadik.knigopis.api.BookCoverSearch
@@ -93,13 +94,15 @@ class BooksAdapter(
             }
         }
         .bind<ImageView>(R.id.book_image) {
-            alpha = 0f
             coverSearch.search(books[it])
                 .subscribe({ coverUrl ->
                     Glide.with(context)
                         .load(coverUrl)
-                        .doOnSuccess { show() }
-                        .apply(RequestOptions.centerCropTransform())
+                        .apply(
+                            RequestOptions.centerCropTransform()
+                                .placeholder(R.color.image_placeholder_color)
+                        )
+                        .transition(DrawableTransitionOptions.withCrossFade())
                         .into(this)
                 }, {
                     logError("cannot load thumbnail", it)

+ 0 - 18
app/src/main/java/me/vadik/knigopis/adapters/UsersAdapter.kt

@@ -1,18 +0,0 @@
-package me.vadik.knigopis.adapters
-
-import android.widget.TextView
-import me.vadik.knigopis.R
-import me.vadik.knigopis.model.User
-
-object UsersAdapter {
-    fun create(users: List<User>) = Adapter(users, { R.layout.user })
-        .bind<TextView>(R.id.user_name) {
-            text = users[it].nickname
-        }
-        .bind<TextView>(R.id.book_count) {
-            val user = users[it]
-            text = user.booksCount.toString()
-            setTextColor(user.color)
-        }
-        .get()
-}

+ 20 - 0
app/src/main/java/me/vadik/knigopis/adapters/notes/NoteViewHolder.kt

@@ -0,0 +1,20 @@
+package me.vadik.knigopis.adapters.users
+
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import kotlinx.android.synthetic.main.note.view.*
+
+class NoteViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
+
+    var nickname: String
+        get() = view.userNickname.text.toString()
+        set(value) {
+            view.userNickname.text = value
+        }
+
+    var notes: String
+        get() = view.userNotes.text.toString()
+        set(value) {
+            view.userNotes.text = value
+        }
+}

+ 28 - 0
app/src/main/java/me/vadik/knigopis/adapters/notes/NotesAdapter.kt

@@ -0,0 +1,28 @@
+package me.vadik.knigopis.adapters.notes
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewGroup
+import me.vadik.knigopis.R
+import me.vadik.knigopis.adapters.users.NoteViewHolder
+import me.vadik.knigopis.inflate
+import me.vadik.knigopis.model.note.Note
+
+class NotesAdapter(
+    private val notes: List<Note>
+) : RecyclerView.Adapter<NoteViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
+        val view = parent.inflate(R.layout.note)
+        return NoteViewHolder(view)
+    }
+
+    override fun getItemCount(): Int {
+        return notes.size
+    }
+
+    override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
+        val note = notes[position]
+        holder.nickname = note.user.nickname
+        holder.notes = "${note.notes} // \"${note.title}\" (${note.author})"
+    }
+}

+ 37 - 0
app/src/main/java/me/vadik/knigopis/adapters/users/UserViewHolder.kt

@@ -0,0 +1,37 @@
+package me.vadik.knigopis.adapters.users
+
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.request.RequestOptions
+import kotlinx.android.synthetic.main.user.view.*
+import me.vadik.knigopis.R
+
+class UserViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
+
+    var avatarUrl: String? = null
+        set(value) {
+            field = value
+            Glide.with(view.context)
+                .load(avatarUrl)
+                .apply(
+                    RequestOptions.circleCropTransform()
+                        .placeholder(R.drawable.oval_placeholder_background)
+                )
+                .transition(DrawableTransitionOptions.withCrossFade())
+                .into(view.userAvatar)
+        }
+
+    var nickname: String
+        get() = view.userNickname.text.toString()
+        set(value) {
+            view.userNickname.text = value
+        }
+
+    var profile: String
+        get() = view.userProfile.text.toString()
+        set(value) {
+            view.userProfile.text = value
+        }
+}

+ 31 - 0
app/src/main/java/me/vadik/knigopis/adapters/users/UsersAdapter.kt

@@ -0,0 +1,31 @@
+package me.vadik.knigopis.adapters.users
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewGroup
+import me.vadik.knigopis.R
+import me.vadik.knigopis.inflate
+import me.vadik.knigopis.model.subscription.Subscription
+
+class UsersAdapter(
+    private val users: List<Subscription>
+) : RecyclerView.Adapter<UserViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
+        val view = parent.inflate(R.layout.user)
+        return UserViewHolder(view)
+    }
+
+    override fun getItemCount(): Int {
+        return users.size
+    }
+
+    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
+        val user = users[position]
+        with(user.subUser) {
+            holder.avatarUrl = photo
+            val booksRead = "$booksCount (+${booksCount - user.lastBooksCount})"
+            holder.nickname = nickname + " // " + booksRead
+            holder.profile = profile ?: identity ?: ""
+        }
+    }
+}

+ 8 - 1
app/src/main/java/me/vadik/knigopis/api/Endpoint.kt

@@ -3,6 +3,8 @@ package me.vadik.knigopis.api
 import io.reactivex.Completable
 import io.reactivex.Single
 import me.vadik.knigopis.model.*
+import me.vadik.knigopis.model.note.Note
+import me.vadik.knigopis.model.subscription.Subscription
 import retrofit2.http.*
 
 interface Endpoint {
@@ -64,5 +66,10 @@ interface Endpoint {
     fun getLatestUsers(): Single<Map<String, User>>
 
     @GET("books/latest-notes")
-    fun getLatestBooksWithNotes(): Single<Map<String, FinishedBook>>
+    fun getLatestBooksWithNotes(): Single<Map<String, Note>>
+
+    @GET("subscriptions")
+    fun getSubscriptions(
+        @Query("access-token") accessToken: String
+    ): Single<List<Subscription>>
 }

+ 7 - 0
app/src/main/java/me/vadik/knigopis/model/note/Identity.kt

@@ -0,0 +1,7 @@
+package me.vadik.knigopis.model.note
+
+class Identity(
+    val id: String,
+    val nickname: String,
+    val booksCount: Int
+)

+ 9 - 0
app/src/main/java/me/vadik/knigopis/model/note/Note.kt

@@ -0,0 +1,9 @@
+package me.vadik.knigopis.model.note
+
+class Note(
+    val id: String,
+    val title: String,
+    val author: String,
+    val notes: String,
+    val user: Identity
+)

+ 10 - 0
app/src/main/java/me/vadik/knigopis/model/subscription/SubUser.kt

@@ -0,0 +1,10 @@
+package me.vadik.knigopis.model.subscription
+
+class SubUser(
+    val id: String,
+    val nickname: String,
+    val photo: String?,
+    val profile: String?,
+    val identity: String?,
+    val booksCount: Int
+)

+ 6 - 0
app/src/main/java/me/vadik/knigopis/model/subscription/Subscription.kt

@@ -0,0 +1,6 @@
+package me.vadik.knigopis.model.subscription
+
+class Subscription(
+    val subUser: SubUser,
+    val lastBooksCount: Int
+)

+ 5 - 0
app/src/main/res/drawable/oval_placeholder_background.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="@color/image_placeholder_color" />
+</shape>

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

@@ -33,6 +33,23 @@
             android:layout_height="match_parent"
             tools:listitem="@layout/book" />
 
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/usersRecyclerView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            tools:listitem="@layout/user"
+            tools:visibility="gone" />
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/notesRecyclerView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipToPadding="false"
+            android:paddingBottom="8dp"
+            android:paddingTop="8dp"
+            tools:listitem="@layout/note"
+            tools:visibility="gone" />
+
         <ProgressBar
             android:id="@+id/booksProgressBar"
             android:layout_width="match_parent"

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

@@ -23,10 +23,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" />
+        android:background="@color/image_placeholder_color"
+        tools:ignore="ContentDescription" />
 
     <LinearLayout
         android:layout_width="match_parent"

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 25 - 0
app/src/main/res/layout/note.xml


+ 41 - 16
app/src/main/res/layout/user.xml

@@ -1,23 +1,48 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
+    android:layout_height="72dp"
+    android:foreground="?android:attr/selectableItemBackground">
 
-    <TextView
-        android:id="@+id/user_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:padding="16dp"
-        tools:text="Вася Пупкин" />
+    <ImageView
+        android:id="@+id/userAvatar"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="16dp"
+        android:background="@drawable/oval_placeholder_background"
+        tools:ignore="ContentDescription" />
 
-    <TextView
-        android:id="@+id/book_count"
-        android:layout_width="wrap_content"
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:padding="16dp"
-        tools:text="100" />
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingEnd="16dp"
+        android:paddingStart="72dp">
+
+        <TextView
+            android:id="@+id/userNickname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="@android:color/primary_text_light"
+            android:textSize="16sp"
+            tools:text="Павел Дуров" />
+
+        <TextView
+            android:id="@+id/userProfile"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:autoLink="web"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="@android:color/tertiary_text_light"
+            android:textSize="14sp"
+            tools:text="https://vk.com/id1" />
+
+    </LinearLayout>
 
-</LinearLayout>
+</FrameLayout>

+ 3 - 9
app/src/main/res/menu/options.xml

@@ -1,20 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
-<menu
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <item
         android:id="@+id/option_login"
-        android:visible="false"/>
-
-    <item
-        android:id="@+id/option_refresh"
-        android:title="@string/option_refresh"
-        app:showAsAction="never"/>
+        android:visible="false" />
 
     <item
         android:id="@+id/option_about"
         android:title="@string/option_about"
-        app:showAsAction="never"/>
+        app:showAsAction="never" />
 
 </menu>

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

@@ -5,14 +5,13 @@
     <string name="book_edit_title">Редактировать книгу</string>
 
     <!-- navigation -->
-    <string name="title_home">Мои книги</string>
+    <string name="title_home">Книги</string>
     <string name="title_users">Подписки</string>
-    <string name="title_notes">Примечания</string>
+    <string name="title_notes">Заметки</string>
 
     <!-- menu -->
     <string name="option_login">Войти</string>
     <string name="option_logout">Выйти</string>
-    <string name="option_refresh">Обновить</string>
     <string name="option_about">О приложении</string>
 
     <!-- books -->

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

@@ -15,11 +15,14 @@
     <color name="colorPrimaryDark">#512DA8</color>
     <color name="colorPrimaryLight">#9575CD</color>
     <color name="colorPrimaryLightest">#B39DDB</color>
+    <color name="colorPrimary100">#D1C4E9</color>
+    <color name="colorPrimary50">#EDE7F6</color>
     <color name="colorAccent">#FF3D00</color>
 
     <color name="white">#FFFFFF</color>
     <color name="white_12">#E1E1E1</color>
     <color name="black_12">#1F1F1F</color>
     <color name="black_54">#757575</color>
+    <color name="image_placeholder_color">@color/colorPrimary50</color>
     <color name="ic_launcher_background">@color/colorPrimaryDark</color>
 </resources>

+ 1 - 2
app/src/main/res/values/strings.xml

@@ -6,13 +6,12 @@
 
     <!-- navigation -->
     <string name="title_home">Books</string>
-    <string name="title_users">Followers</string>
+    <string name="title_users">Following</string>
     <string name="title_notes">Notes</string>
 
     <!-- menu -->
     <string name="option_login">Login</string>
     <string name="option_logout">Logout</string>
-    <string name="option_refresh">Refresh</string>
     <string name="option_about">About</string>
 
     <!-- books -->

+ 2 - 2
app/src/main/res/values/styles.xml

@@ -8,8 +8,8 @@
     </style>
 
     <style name="DevTheme" parent="AppTheme">
-        <item name="colorPrimary">#9C27B0</item>
-        <item name="colorPrimaryDark">#7B1FA2</item>
+        <!--<item name="colorPrimary">#9C27B0</item>-->
+        <!--<item name="colorPrimaryDark">#7B1FA2</item>-->
     </style>
 
     <style name="AppBarTheme">

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است