2019-03-16 13:36:16 +01:00
|
|
|
/* Copyright 2017 Andrew Dawson
|
|
|
|
*
|
|
|
|
* This file is a part of Tusky.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
|
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
|
|
* Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
|
|
|
* see <http://www.gnu.org/licenses>.
|
|
|
|
*/
|
|
|
|
|
2018-01-06 19:01:37 +01:00
|
|
|
package com.keylesspalace.tusky
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
import android.app.Dialog
|
2018-01-06 19:01:37 +01:00
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2021-06-28 21:13:24 +02:00
|
|
|
import android.widget.EditText
|
|
|
|
import android.widget.FrameLayout
|
|
|
|
import android.widget.ImageButton
|
|
|
|
import android.widget.PopupMenu
|
|
|
|
import android.widget.TextView
|
2021-03-07 19:05:51 +01:00
|
|
|
import androidx.activity.viewModels
|
2019-03-16 13:36:16 +01:00
|
|
|
import androidx.annotation.StringRes
|
|
|
|
import androidx.appcompat.app.AlertDialog
|
2022-05-18 18:45:35 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-06-28 21:13:24 +02:00
|
|
|
import androidx.recyclerview.widget.DiffUtil
|
|
|
|
import androidx.recyclerview.widget.DividerItemDecoration
|
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
2019-03-16 13:36:16 +01:00
|
|
|
import androidx.recyclerview.widget.ListAdapter
|
2021-06-28 21:13:24 +02:00
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2019-03-16 13:36:16 +01:00
|
|
|
import at.connyduck.sparkbutton.helpers.Utils
|
2022-12-31 13:01:35 +01:00
|
|
|
import com.google.android.material.color.MaterialColors
|
2019-03-16 13:36:16 +01:00
|
|
|
import com.google.android.material.snackbar.Snackbar
|
2021-03-07 19:05:51 +01:00
|
|
|
import com.keylesspalace.tusky.databinding.ActivityListsBinding
|
2018-03-27 19:47:00 +02:00
|
|
|
import com.keylesspalace.tusky.di.Injectable
|
2019-03-16 13:36:16 +01:00
|
|
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
2018-01-06 19:01:37 +01:00
|
|
|
import com.keylesspalace.tusky.entity.MastoList
|
2021-06-28 21:13:24 +02:00
|
|
|
import com.keylesspalace.tusky.util.hide
|
|
|
|
import com.keylesspalace.tusky.util.onTextChanged
|
|
|
|
import com.keylesspalace.tusky.util.show
|
|
|
|
import com.keylesspalace.tusky.util.viewBinding
|
|
|
|
import com.keylesspalace.tusky.util.visible
|
2019-03-16 13:36:16 +01:00
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel
|
2021-06-28 21:13:24 +02:00
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.Event
|
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.ERROR_NETWORK
|
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.ERROR_OTHER
|
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.INITIAL
|
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADED
|
|
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADING
|
2018-01-06 19:01:37 +01:00
|
|
|
import com.mikepenz.iconics.IconicsDrawable
|
2020-04-15 18:57:53 +02:00
|
|
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
|
|
|
import com.mikepenz.iconics.utils.colorInt
|
|
|
|
import com.mikepenz.iconics.utils.sizeDp
|
2019-03-16 13:36:16 +01:00
|
|
|
import dagger.android.DispatchingAndroidInjector
|
2019-06-09 19:48:04 +02:00
|
|
|
import dagger.android.HasAndroidInjector
|
2022-05-18 18:45:35 +02:00
|
|
|
import kotlinx.coroutines.launch
|
2018-03-27 19:47:00 +02:00
|
|
|
import javax.inject.Inject
|
2018-01-06 19:01:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Created by charlag on 1/4/18.
|
|
|
|
*/
|
|
|
|
|
2019-06-09 19:48:04 +02:00
|
|
|
class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
@Inject
|
2019-03-16 13:36:16 +01:00
|
|
|
lateinit var viewModelFactory: ViewModelFactory
|
|
|
|
|
|
|
|
@Inject
|
2019-06-09 19:48:04 +02:00
|
|
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
2018-03-27 19:47:00 +02:00
|
|
|
|
2021-03-07 19:05:51 +01:00
|
|
|
private val viewModel: ListsViewModel by viewModels { viewModelFactory }
|
|
|
|
|
|
|
|
private val binding by viewBinding(ActivityListsBinding::inflate)
|
|
|
|
|
2018-01-06 19:01:37 +01:00
|
|
|
private val adapter = ListsAdapter()
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
|
2021-03-07 19:05:51 +01:00
|
|
|
setContentView(binding.root)
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2021-03-07 19:05:51 +01:00
|
|
|
setSupportActionBar(binding.includedToolbar.toolbar)
|
2019-03-16 13:36:16 +01:00
|
|
|
supportActionBar?.apply {
|
|
|
|
title = getString(R.string.title_lists)
|
|
|
|
setDisplayHomeAsUpEnabled(true)
|
|
|
|
setDisplayShowHomeEnabled(true)
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.listsRecycler.adapter = adapter
|
|
|
|
binding.listsRecycler.layoutManager = LinearLayoutManager(this)
|
|
|
|
binding.listsRecycler.addItemDecoration(
|
2021-06-28 21:13:24 +02:00
|
|
|
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
|
|
|
|
)
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2022-05-18 18:45:35 +02:00
|
|
|
lifecycleScope.launch {
|
|
|
|
viewModel.state.collect(this@ListsActivity::update)
|
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
viewModel.retryLoading()
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.addListButton.setOnClickListener {
|
2019-03-16 13:36:16 +01:00
|
|
|
showlistNameDialog(null)
|
|
|
|
}
|
|
|
|
|
2022-05-18 18:45:35 +02:00
|
|
|
lifecycleScope.launch {
|
|
|
|
viewModel.events.collect { event ->
|
2021-06-28 21:13:24 +02:00
|
|
|
when (event) {
|
|
|
|
Event.CREATE_ERROR -> showMessage(R.string.error_create_list)
|
|
|
|
Event.RENAME_ERROR -> showMessage(R.string.error_rename_list)
|
|
|
|
Event.DELETE_ERROR -> showMessage(R.string.error_delete_list)
|
2019-03-16 13:36:16 +01:00
|
|
|
}
|
2021-06-28 21:13:24 +02:00
|
|
|
}
|
2022-05-18 18:45:35 +02:00
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
private fun showlistNameDialog(list: MastoList?) {
|
|
|
|
val layout = FrameLayout(this)
|
|
|
|
val editText = EditText(this)
|
|
|
|
editText.setHint(R.string.hint_list_name)
|
|
|
|
layout.addView(editText)
|
|
|
|
val margin = Utils.dpToPx(this, 8)
|
|
|
|
(editText.layoutParams as ViewGroup.MarginLayoutParams)
|
2021-06-28 21:13:24 +02:00
|
|
|
.setMargins(margin, margin, margin, 0)
|
2019-03-16 13:36:16 +01:00
|
|
|
|
|
|
|
val dialog = AlertDialog.Builder(this)
|
2021-06-28 21:13:24 +02:00
|
|
|
.setView(layout)
|
|
|
|
.setPositiveButton(
|
|
|
|
if (list == null) R.string.action_create_list
|
|
|
|
else R.string.action_rename_list
|
|
|
|
) { _, _ ->
|
|
|
|
onPickedDialogName(editText.text, list?.id)
|
|
|
|
}
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
.show()
|
2019-03-16 13:36:16 +01:00
|
|
|
|
|
|
|
val positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE)
|
|
|
|
editText.onTextChanged { s, _, _, _ ->
|
2021-01-31 20:27:02 +01:00
|
|
|
positiveButton.isEnabled = s.isNotBlank()
|
2019-03-16 13:36:16 +01:00
|
|
|
}
|
|
|
|
editText.setText(list?.title)
|
|
|
|
editText.text?.let { editText.setSelection(it.length) }
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2021-01-18 11:40:13 +01:00
|
|
|
private fun showListDeleteDialog(list: MastoList) {
|
|
|
|
AlertDialog.Builder(this)
|
2021-06-28 21:13:24 +02:00
|
|
|
.setMessage(getString(R.string.dialog_delete_list_warning, list.title))
|
|
|
|
.setPositiveButton(R.string.action_delete) { _, _ ->
|
|
|
|
viewModel.deleteList(list.id)
|
|
|
|
}
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
.show()
|
2021-01-18 11:40:13 +01:00
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
private fun update(state: ListsViewModel.State) {
|
|
|
|
adapter.submitList(state.lists)
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.progressBar.visible(state.loadingState == LOADING)
|
2019-01-28 19:02:31 +01:00
|
|
|
when (state.loadingState) {
|
2021-03-07 19:05:51 +01:00
|
|
|
INITIAL, LOADING -> binding.messageView.hide()
|
2019-01-28 19:02:31 +01:00
|
|
|
ERROR_NETWORK -> {
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.messageView.show()
|
|
|
|
binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
2019-01-28 19:02:31 +01:00
|
|
|
viewModel.retryLoading()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ERROR_OTHER -> {
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.messageView.show()
|
|
|
|
binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
2019-01-28 19:02:31 +01:00
|
|
|
viewModel.retryLoading()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOADED ->
|
|
|
|
if (state.lists.isEmpty()) {
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.messageView.show()
|
2021-06-28 21:13:24 +02:00
|
|
|
binding.messageView.setup(
|
|
|
|
R.drawable.elephant_friend_empty, R.string.message_empty,
|
|
|
|
null
|
|
|
|
)
|
2019-01-28 19:02:31 +01:00
|
|
|
} else {
|
2021-03-07 19:05:51 +01:00
|
|
|
binding.messageView.hide()
|
2019-01-28 19:02:31 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
private fun showMessage(@StringRes messageId: Int) {
|
|
|
|
Snackbar.make(
|
2021-06-28 21:13:24 +02:00
|
|
|
binding.listsRecycler, messageId, Snackbar.LENGTH_SHORT
|
2019-03-16 13:36:16 +01:00
|
|
|
).show()
|
|
|
|
}
|
|
|
|
|
2022-05-17 19:55:26 +02:00
|
|
|
private fun onListSelected(listId: String, listTitle: String) {
|
2018-07-31 21:25:25 +02:00
|
|
|
startActivityWithSlideInAnimation(
|
2022-05-17 19:55:26 +02:00
|
|
|
StatusListActivity.newListIntent(this, listId, listTitle)
|
2021-06-28 21:13:24 +02:00
|
|
|
)
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
private fun openListSettings(list: MastoList) {
|
|
|
|
AccountsInListFragment.newInstance(list.id, list.title).show(supportFragmentManager, null)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun renameListDialog(list: MastoList) {
|
|
|
|
showlistNameDialog(list)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onMore(list: MastoList, view: View) {
|
|
|
|
PopupMenu(view.context, view).apply {
|
|
|
|
inflate(R.menu.list_actions)
|
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
when (item.itemId) {
|
|
|
|
R.id.list_edit -> openListSettings(list)
|
|
|
|
R.id.list_rename -> renameListDialog(list)
|
2021-01-18 11:40:13 +01:00
|
|
|
R.id.list_delete -> showListDeleteDialog(list)
|
2019-03-16 13:36:16 +01:00
|
|
|
else -> return@setOnMenuItemClickListener false
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private object ListsDiffer : DiffUtil.ItemCallback<MastoList>() {
|
|
|
|
override fun areItemsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
|
|
|
|
return oldItem.id == newItem.id
|
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
override fun areContentsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
|
|
|
|
return oldItem == newItem
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
2019-03-16 13:36:16 +01:00
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
|
2021-06-28 21:13:24 +02:00
|
|
|
private inner class ListsAdapter :
|
|
|
|
ListAdapter<MastoList, ListsAdapter.ListViewHolder>(ListsDiffer) {
|
2018-01-06 19:01:37 +01:00
|
|
|
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
|
|
|
return LayoutInflater.from(parent.context).inflate(R.layout.item_list, parent, false)
|
2021-06-28 21:13:24 +02:00
|
|
|
.let(this::ListViewHolder)
|
|
|
|
.apply {
|
2022-12-31 13:01:35 +01:00
|
|
|
val iconColor = MaterialColors.getColor(nameTextView, android.R.attr.textColorTertiary)
|
2021-06-28 21:13:24 +02:00
|
|
|
val context = nameTextView.context
|
|
|
|
val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).apply { sizeDp = 20; colorInt = iconColor }
|
|
|
|
|
|
|
|
nameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
|
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
2019-03-16 13:36:16 +01:00
|
|
|
holder.nameTextView.text = getItem(position).title
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2021-06-28 21:13:24 +02:00
|
|
|
private inner class ListViewHolder(view: View) :
|
|
|
|
RecyclerView.ViewHolder(view),
|
|
|
|
View.OnClickListener {
|
2018-01-06 19:01:37 +01:00
|
|
|
val nameTextView: TextView = view.findViewById(R.id.list_name_textview)
|
2019-03-16 13:36:16 +01:00
|
|
|
val moreButton: ImageButton = view.findViewById(R.id.editListButton)
|
2018-01-06 19:01:37 +01:00
|
|
|
|
|
|
|
init {
|
|
|
|
view.setOnClickListener(this)
|
2019-03-16 13:36:16 +01:00
|
|
|
moreButton.setOnClickListener(this)
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-16 13:36:16 +01:00
|
|
|
override fun onClick(v: View) {
|
|
|
|
if (v == itemView) {
|
2022-05-17 19:55:26 +02:00
|
|
|
val list = getItem(bindingAdapterPosition)
|
|
|
|
onListSelected(list.id, list.title)
|
2019-03-16 13:36:16 +01:00
|
|
|
} else {
|
2021-04-10 20:30:44 +02:00
|
|
|
onMore(getItem(bindingAdapterPosition), v)
|
2019-03-16 13:36:16 +01:00
|
|
|
}
|
2018-01-06 19:01:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-16 13:36:16 +01:00
|
|
|
|
|
|
|
private fun onPickedDialogName(name: CharSequence, listId: String?) {
|
|
|
|
if (listId == null) {
|
|
|
|
viewModel.createNewList(name.toString())
|
|
|
|
} else {
|
|
|
|
viewModel.renameList(listId, name.toString())
|
|
|
|
}
|
|
|
|
}
|
2021-03-07 19:05:51 +01:00
|
|
|
|
|
|
|
override fun androidInjector() = dispatchingAndroidInjector
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
fun newIntent(context: Context) = Intent(context, ListsActivity::class.java)
|
|
|
|
}
|
2021-06-28 21:13:24 +02:00
|
|
|
}
|