ソースを参照

Added initial implementation of user screen

sirekanyan 7 年 前
コミット
a850caa14c

+ 4 - 0
app/src/main/AndroidManifest.xml

@@ -29,6 +29,10 @@
 
         <activity android:name=".BookActivity"/>
 
+        <activity
+            android:name=".user.UserActivity"
+            android:parentActivityName=".MainActivity"/>
+
         <activity
             android:name="ru.ulogin.sdk.UloginAuthActivity"
             android:configChanges="orientation|screenSize"/>

+ 3 - 0
app/src/main/java/me/vadik/knigopis/MainActivity.kt

@@ -31,6 +31,7 @@ import me.vadik.knigopis.model.CurrentTab.*
 import me.vadik.knigopis.model.note.Identity
 import me.vadik.knigopis.model.note.Note
 import me.vadik.knigopis.model.subscription.Subscription
+import me.vadik.knigopis.user.createUserIntent
 import retrofit2.HttpException
 
 private const val ULOGIN_REQUEST_CODE = 0
@@ -130,9 +131,11 @@ class MainActivity : AppCompatActivity(), Router {
     }
 
     override fun openUserScreen(user: Subscription) {
+        startActivity(createUserIntent(user))
     }
 
     override fun openUserScreen(user: Identity) {
+        startActivity(createUserIntent(user))
     }
 
     override fun openBrowser(uri: Uri) {

+ 27 - 0
app/src/main/java/me/vadik/knigopis/adapters/books/BookViewHolder.kt

@@ -0,0 +1,27 @@
+package me.vadik.knigopis.adapters.books
+
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import kotlinx.android.synthetic.main.user_book.view.*
+import me.vadik.knigopis.hideNow
+import me.vadik.knigopis.showNow
+
+class BookViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
+
+    var title: String
+        get() = view.bookTitle.text.toString()
+        set(value) {
+            view.bookTitle.text = value
+        }
+
+    var notes: String
+        get() = view.bookNotes.text.toString()
+        set(value) {
+            if (value.isEmpty()) {
+                view.bookNotes.hideNow()
+            } else {
+                view.bookNotes.showNow()
+                view.bookNotes.text = value
+            }
+        }
+}

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

@@ -0,0 +1,23 @@
+package me.vadik.knigopis.adapters.books
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewGroup
+import me.vadik.knigopis.R
+import me.vadik.knigopis.inflate
+
+class BooksAdapter(
+    private val books: List<UserBook>
+) : RecyclerView.Adapter<BookViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+        BookViewHolder(parent.inflate(R.layout.user_book))
+
+    override fun getItemCount() =
+        books.size
+
+    override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
+        val book = books[position]
+        holder.title = "\"${book.title}\" — ${book.author}"
+        holder.notes = book.notes
+    }
+}

+ 11 - 0
app/src/main/java/me/vadik/knigopis/adapters/books/UserBook.kt

@@ -0,0 +1,11 @@
+package me.vadik.knigopis.adapters.books
+
+class UserBook(
+    val id: String,
+    val title: String,
+    val author: String,
+    val readDay: String,
+    val readMonth: String,
+    val readYear: String,
+    val notes: String
+)

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

@@ -23,11 +23,11 @@ class UsersAdapter(
 
     override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
         val user = users[position]
-        with(user.subUser) {
-            holder.avatarUrl = photo
-            val booksRead = "$booksCount (+${user.recentBooksCount})"
-            holder.nickname = nickname + " // " + booksRead
-        }
+        holder.nickname = user.subUser.nickname
+        holder.avatarUrl = user.subUser.photo
+        holder.profile = "${user.subUser.booksCount} прочитано" + user.newBooksCount?.let {
+            " (+$it новых)"
+        }.orEmpty()
         holder.view.setOnClickListener {
             router.openUserScreen(user)
         }

+ 16 - 0
app/src/main/java/me/vadik/knigopis/api/Endpoint.kt

@@ -2,6 +2,7 @@ package me.vadik.knigopis.api
 
 import io.reactivex.Completable
 import io.reactivex.Single
+import me.vadik.knigopis.adapters.books.UserBook
 import me.vadik.knigopis.model.*
 import me.vadik.knigopis.model.note.Note
 import me.vadik.knigopis.model.subscription.Subscription
@@ -63,4 +64,19 @@ interface Endpoint {
     fun getSubscriptions(
         @Query("access-token") accessToken: String
     ): Single<List<Subscription>>
+
+    @GET("users/{id}/books")
+    fun getUserBooks(@Path("id") userId: String): Single<List<UserBook>>
+
+    @POST("subscriptions/{subUserId}")
+    fun createSubscription(
+        @Path("subUserId") userId: String,
+        @Query("access-token") accessToken: String
+    ): Single<Any>
+
+    @DELETE("subscriptions/{subUserId}")
+    fun deleteSubscription(
+        @Path("subUserId") userId: String,
+        @Query("access-token") accessToken: String
+    ): Single<Any>
 }

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

@@ -28,7 +28,7 @@ import io.reactivex.schedulers.Schedulers
 private const val TAG = "Knigopis"
 private val HTTP_SCHEMES = setOf("http", "https")
 
-fun Context.startActivityOrElse(intent: Intent, onError: () -> Unit) {
+inline fun Context.startActivityOrElse(intent: Intent, onError: () -> Unit) {
     if (packageManager.resolveActivity(intent, 0) == null) {
         onError()
     } else {

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

@@ -4,5 +4,5 @@ class Subscription(
     val subUser: SubUser,
     private val lastBooksCount: Int
 ) {
-    val recentBooksCount get() = subUser.booksCount - lastBooksCount
+    val newBooksCount get() = (subUser.booksCount - lastBooksCount).takeIf { it > 0 }
 }

+ 88 - 0
app/src/main/java/me/vadik/knigopis/user/UserActivity.kt

@@ -0,0 +1,88 @@
+package me.vadik.knigopis.user
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.design.widget.Snackbar
+import android.support.v7.app.AppCompatActivity
+import android.support.v7.widget.LinearLayoutManager
+import kotlinx.android.synthetic.main.user_activity.*
+import me.vadik.knigopis.R
+import me.vadik.knigopis.adapters.books.BooksAdapter
+import me.vadik.knigopis.adapters.books.UserBook
+import me.vadik.knigopis.api.Endpoint
+import me.vadik.knigopis.app
+import me.vadik.knigopis.auth.KAuth
+import me.vadik.knigopis.auth.KAuthImpl
+import me.vadik.knigopis.io2main
+import me.vadik.knigopis.logError
+import me.vadik.knigopis.model.note.Identity
+import me.vadik.knigopis.model.subscription.Subscription
+
+private const val EXTRA_USER_ID = "me.vadik.knigopis.extra_user_id"
+private const val EXTRA_USER_NAME = "me.vadik.knigopis.extra_user_name"
+private const val EXTRA_USER_PHOTO = "me.vadik.knigopis.extra_user_photo"
+
+fun Context.createUserIntent(user: Subscription): Intent =
+    Intent(this, UserActivity::class.java)
+        .putExtra(EXTRA_USER_ID, user.subUser.id)
+        .putExtra(EXTRA_USER_NAME, user.subUser.nickname)
+        .putExtra(EXTRA_USER_PHOTO, user.subUser.photo)
+
+fun Context.createUserIntent(user: Identity): Intent =
+    Intent(this, UserActivity::class.java)
+        .putExtra(EXTRA_USER_ID, user.id)
+        .putExtra(EXTRA_USER_NAME, user.nickname)
+
+class UserActivity : AppCompatActivity() {
+
+    private val api by lazy { app().baseApi.create(Endpoint::class.java) }
+    private val auth by lazy { KAuthImpl(applicationContext, api) as KAuth }
+    private val userId by lazy { intent.getStringExtra(EXTRA_USER_ID) }
+    private val books = mutableListOf<UserBook>()
+    private val booksAdapter = BooksAdapter(books)
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.user_activity)
+        toolbar.title = intent.getStringExtra(EXTRA_USER_NAME)
+        setSupportActionBar(toolbar)
+        fab.setOnClickListener { view ->
+            fab.hide()
+            api.createSubscription(userId, auth.getAccessToken())
+                .io2main()
+                .subscribe({
+                    Snackbar.make(view, "Successfully subscribed", Snackbar.LENGTH_LONG)
+                        .setAction("Action", null).show()
+                }, {
+                    fab.show()
+                    logError("Cannot update subscription", it)
+                })
+        }
+        supportActionBar?.setDisplayHomeAsUpEnabled(true)
+        userBooksRecyclerView.layoutManager = LinearLayoutManager(this)
+        userBooksRecyclerView.adapter = booksAdapter
+    }
+
+    override fun onStart() {
+        super.onStart()
+        api.getUserBooks(userId)
+            .io2main()
+            .subscribe({
+                books.clear()
+                books.addAll(it)
+                booksAdapter.notifyDataSetChanged()
+            }, {
+                logError("Cannot load user books", it)
+            })
+        api.getSubscriptions(auth.getAccessToken())
+            .io2main()
+            .subscribe({ subscriptions ->
+                if (subscriptions.none { it.subUser.id == userId }) {
+                    fab.show()
+                }
+            }, {
+                logError("Cannot update subscription", it)
+            })
+    }
+}

+ 9 - 0
app/src/main/res/drawable/ic_person_add.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
+</vector>

+ 59 - 0
app/src/main/res/layout/user_activity.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white"
+    android:fitsSystemWindows="true"
+    tools:context="me.vadik.knigopis.user.UserActivity">
+
+    <android.support.design.widget.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="180dp"
+        android:fitsSystemWindows="true"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+        <android.support.design.widget.CollapsingToolbarLayout
+            android:id="@+id/toolbar_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fitsSystemWindows="true"
+            app:contentScrim="?attr/colorPrimary"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed"
+            app:toolbarId="@+id/toolbar">
+
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                app:layout_collapseMode="pin"
+                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
+
+        </android.support.design.widget.CollapsingToolbarLayout>
+
+    </android.support.design.widget.AppBarLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/userBooksRecyclerView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false"
+        android:paddingBottom="8dp"
+        android:paddingTop="8dp"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior"
+        tools:listitem="@layout/user_book" />
+
+    <android.support.design.widget.FloatingActionButton
+        android:id="@+id/fab"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:tint="@color/white"
+        android:visibility="gone"
+        app:layout_anchor="@id/app_bar"
+        app:layout_anchorGravity="bottom|end"
+        app:srcCompat="@drawable/ic_person_add" />
+
+</android.support.design.widget.CoordinatorLayout>

+ 31 - 0
app/src/main/res/layout/user_book.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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:clickable="true"
+    android:focusable="true"
+    android:foreground="?android:attr/selectableItemBackground"
+    android:orientation="vertical"
+    android:paddingBottom="8dp"
+    android:paddingEnd="16dp"
+    android:paddingStart="16dp"
+    android:paddingTop="8dp">
+
+    <TextView
+        android:id="@+id/bookTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@android:color/primary_text_light"
+        android:textSize="16sp"
+        tools:text="'Мастер и Маргарита' — Михаил Булгаков" />
+
+    <TextView
+        android:id="@+id/bookNotes"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@android:color/tertiary_text_light"
+        android:textSize="14sp"
+        tools:text="Михаил БулгаковМихаил БулгаковМихаил БулгаковМихаил БулгаковМихаил БулгаковМихаил БулгаковМихаил БулгаковМихаил Булгаков" />
+
+</LinearLayout>

ファイルの差分が大きいため隠しています
+ 113 - 0
examples/books/latest-notes.json


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません