Sfoglia il codice sorgente

Added deleting books with confirmation

sirekanyan 8 anni fa
parent
commit
8e0f16c916

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

@@ -1,5 +1,7 @@
 package me.vadik.knigopis
 
+import android.content.Context
+import android.content.Intent
 import android.os.Bundle
 import android.support.v7.app.AppCompatActivity
 import android.support.v7.widget.Toolbar
@@ -20,6 +22,8 @@ import me.vadik.knigopis.auth.KAuthImpl
 import me.vadik.knigopis.model.FinishedBookToSend
 import me.vadik.knigopis.model.PlannedBookToSend
 
+fun Context.createBookIntent() = Intent(this, BookActivity::class.java)
+
 class BookActivity : AppCompatActivity() {
 
   private val api by lazy { app().baseApi.create(Endpoint::class.java) }

+ 5 - 4
app/src/main/java/me/vadik/knigopis/MainActivity.kt

@@ -26,8 +26,8 @@ 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<FinishedBook>()
-  private val plannedBooks = mutableListOf<PlannedBook>()
+  private val finishedBooks = mutableListOf<Book>()
+  private val plannedBooks = mutableListOf<Book>()
   private val booksAdapter by lazy {
     BooksAdapter(BookCoverSearchImpl(
         app().imageApi.create(ImageEndpoint::class.java),
@@ -49,7 +49,7 @@ class MainActivity : AppCompatActivity() {
     initNavigationView(findView(R.id.navigation))
     initToolbar(findView(R.id.toolbar))
     fab.setOnClickListener {
-      startActivity(Intent(this, BookActivity::class.java))
+      startActivity(createBookIntent())
     }
   }
 
@@ -140,7 +140,8 @@ class MainActivity : AppCompatActivity() {
           allBooks.addAll(it)
           allBooksAdapter.notifyDataSetChanged()
         }, {
-          logError("cannot load finished books", it)
+          toast("Не удалось загрузить книги")
+          logError("cannot load books", it)
         })
   }
 

+ 18 - 9
app/src/main/java/me/vadik/knigopis/adapters/Adapter.kt

@@ -12,15 +12,8 @@ class Adapter<T>(
 ) {
 
   val binders = mutableMapOf<@IdRes Int, (View, Int) -> Unit>()
-
-  inline fun <reified V : View> bind(@IdRes id: Int, crossinline binder: V.(Int) -> Unit): Adapter<T> {
-    binders[id] = { view, position ->
-      binder(view as V, position)
-    }
-    return this
-  }
-
-  fun build() = object : RecyclerView.Adapter<ViewsHolder>() {
+  @Suppress("MemberVisibilityCanPrivate")
+  val recyclerViewAdapter = object : RecyclerView.Adapter<ViewsHolder>() {
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
         parent.inflate(viewType).let { rootView ->
           ViewsHolder(rootView, binders.mapValues { (key, _) ->
@@ -39,6 +32,22 @@ class Adapter<T>(
 
     override fun getItemViewType(position: Int) = layout(items[position])
   }
+
+  inline fun <reified V : View> bind(@IdRes id: Int, crossinline binder: V.(Int) -> Unit): Adapter<T> {
+    binders[id] = { view, position ->
+      binder(view as V, position)
+    }
+    return this
+  }
+
+  inline fun <reified V : View> bind2(@IdRes id: Int, crossinline binder: V.(Int, RecyclerView.Adapter<ViewsHolder>) -> Unit): Adapter<T> {
+    binders[id] = { view, position ->
+      binder(view as V, position, recyclerViewAdapter)
+    }
+    return this
+  }
+
+  fun get() = recyclerViewAdapter
 }
 
 class ViewsHolder(rootView: View, val views: Map<Int, View>) : RecyclerView.ViewHolder(rootView)

+ 55 - 11
app/src/main/java/me/vadik/knigopis/adapters/BooksAdapter.kt

@@ -1,25 +1,73 @@
 package me.vadik.knigopis.adapters
 
+import android.app.AlertDialog
+import android.content.DialogInterface
 import android.view.View
 import android.widget.ImageView
 import android.widget.TextView
 import com.bumptech.glide.Glide
 import com.bumptech.glide.request.RequestOptions
-import me.vadik.knigopis.R
+import me.vadik.knigopis.*
 import me.vadik.knigopis.api.BookCoverSearch
-import me.vadik.knigopis.logError
+import me.vadik.knigopis.api.Endpoint
+import me.vadik.knigopis.auth.KAuth
 import me.vadik.knigopis.model.Book
 import me.vadik.knigopis.model.BookHeader
+import me.vadik.knigopis.model.FinishedBook
+import me.vadik.knigopis.model.PlannedBook
 
-class BooksAdapter(private val coverSearch: BookCoverSearch) {
+class BooksAdapter(
+    private val coverSearch: BookCoverSearch,
+    private val api: Endpoint,
+    private val auth: KAuth
+) {
 
-  fun build(books: List<Book>) = Adapter(books) {
+  fun build(books: MutableList<Book>) = Adapter(books) {
     if (it is BookHeader) {
       R.layout.header
     } else {
       R.layout.book
     }
   }
+      .bind2<View>(R.id.book_item_container) { bookIndex, adapter ->
+        val book = books[bookIndex]
+        val onDeleteConfirmed: (DialogInterface, Int) -> Unit = { dialog, _ ->
+          when (book) {
+            is FinishedBook -> api.deleteFinishedBook(book.id, auth.getAccessToken())
+            is PlannedBook -> api.deletePlannedBook(book.id, auth.getAccessToken())
+            else -> throw UnsupportedOperationException()
+          }
+              .io2main()
+              .subscribe({}, {
+                context.toast(R.string.cannot_delete_book)
+                logError("cannot delete finished book", it)
+              })
+          books.removeAt(bookIndex)
+          adapter.notifyItemRemoved(bookIndex)
+          dialog.dismiss()
+        }
+        val onDeleteClicked: (DialogInterface, Int) -> Unit = { dialog, index ->
+          when (index) {
+            0 -> context.startActivity(context.createBookIntent())
+            1 -> {
+              AlertDialog.Builder(context)
+                  .setTitle(R.string.book_delete_confirmation_title)
+                  .setMessage(context.getString(R.string.book_delete_confirm_text, book.fullTitle))
+                  .setNegativeButton(R.string.book_cancel_delete) { d, _ -> d.dismiss() }
+                  .setPositiveButton(R.string.book_confirm_delete, onDeleteConfirmed)
+                  .show()
+            }
+          }
+          dialog.dismiss()
+        }
+        setOnLongClickListener {
+          AlertDialog.Builder(context)
+              .setTitle(book.fullTitle)
+              .setItems(R.array.book_context_menu, onDeleteClicked)
+              .show()
+          true
+        }
+      }
       .bind<ImageView>(R.id.book_image) {
         coverSearch.search(books[it])
             .subscribe({ coverUrl ->
@@ -32,17 +80,13 @@ class BooksAdapter(private val coverSearch: BookCoverSearch) {
             })
       }
       .bind<TextView>(R.id.book_title) {
-        text = books[it].title
+        text = books[it].titleOrDefault
       }
       .bind<View>(R.id.header_divider) {
         visibility = if (it == 0) View.INVISIBLE else View.VISIBLE
       }
       .bind<TextView>(R.id.book_author) {
-        text = if (books[it].author.isEmpty()) {
-          "(автор не указан)"
-        } else {
-          books[it].author
-        }
+        text = books[it].authorOrDefault
       }
-      .build()
+      .get()
 }

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

@@ -14,5 +14,5 @@ object UsersAdapter {
         text = user.booksCount.toString()
         setTextColor(user.color)
       }
-      .build()
+      .get()
 }

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

@@ -19,6 +19,8 @@ private const val TAG = "Knigopis"
 
 fun Context.toast(message: String) = Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
 
+fun Context.toast(@IdRes messageId: Int) = Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show()
+
 fun Activity.app() = application as App
 
 fun <T : View> Activity.findView(@IdRes id: Int): T = findViewById(id)

+ 1 - 0
app/src/main/java/me/vadik/knigopis/model/Book.kt

@@ -8,4 +8,5 @@ interface Book {
   val author: String
   val titleOrDefault get() = title.orDefault("(без названия)")
   val authorOrDefault get() = author.orDefault("(автор не указан)")
+  val fullTitle get() = titleOrDefault + " — " + authorOrDefault
 }

+ 6 - 6
app/src/main/res/layout/book.xml

@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-    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:id="@+id/book_item_container"
     android:layout_width="match_parent"
-    android:layout_height="72dp">
+    android:layout_height="72dp"
+    android:background="?android:attr/selectableItemBackground">
 
     <ImageView
         android:id="@+id/book_image"
@@ -13,7 +13,7 @@
         android:layout_gravity="center_vertical"
         android:layout_marginStart="16dp"
         tools:ignore="ContentDescription"
-        tools:src="@color/colorAccent"/>
+        tools:src="@color/colorAccent" />
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -31,7 +31,7 @@
             android:maxLines="1"
             android:textColor="@android:color/primary_text_light"
             android:textSize="16sp"
-            tools:text="Мастер и Маргарита"/>
+            tools:text="Мастер и Маргарита" />
 
         <TextView
             android:id="@+id/book_author"
@@ -41,7 +41,7 @@
             android:maxLines="1"
             android:textColor="@android:color/tertiary_text_light"
             android:textSize="14sp"
-            tools:text="Михаил Булгаков"/>
+            tools:text="Михаил Булгаков" />
 
     </LinearLayout>
 

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

@@ -21,4 +21,15 @@
     <string name="book_hint_month">Месяц</string>
     <string name="book_hint_day">День</string>
     <string name="book_read_checkbox">Прочитана</string>
+
+    <!-- book menu -->
+    <array name="book_context_menu">
+        <item>Редактировать</item>
+        <item>Удалить</item>
+    </array>
+    <string name="book_delete_confirmation_title">Подтверждение</string>
+    <string name="book_delete_confirm_text">Удалить книгу \"%s\"?</string>
+    <string name="book_cancel_delete">Отмена</string>
+    <string name="book_confirm_delete">Удалить</string>
+    <string name="cannot_delete_book">Не получилось удалить</string>
 </resources>

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

@@ -21,4 +21,15 @@
     <string name="book_hint_month">Month</string>
     <string name="book_hint_day">Day</string>
     <string name="book_read_checkbox">Done</string>
+
+    <!-- book menu -->
+    <array name="book_context_menu">
+        <item>Edit</item>
+        <item>Delete</item>
+    </array>
+    <string name="book_delete_confirmation_title">Confirmation</string>
+    <string name="book_delete_confirm_text">Are you sure you want to delete \"%s\"?</string>
+    <string name="book_cancel_delete">Cancel</string>
+    <string name="book_confirm_delete">Delete</string>
+    <string name="cannot_delete_book">Cannot delete book</string>
 </resources>