You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
9.9 KiB
275 lines
9.9 KiB
/* 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>.
|
|
*/
|
|
|
|
package com.keylesspalace.tusky
|
|
|
|
import android.app.Dialog
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import android.view.LayoutInflater
|
|
import android.view.MenuItem
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.*
|
|
import androidx.annotation.StringRes
|
|
import androidx.appcompat.app.AlertDialog
|
|
import androidx.recyclerview.widget.*
|
|
import androidx.recyclerview.widget.ListAdapter
|
|
import at.connyduck.sparkbutton.helpers.Utils
|
|
import com.google.android.material.snackbar.Snackbar
|
|
import com.keylesspalace.tusky.di.Injectable
|
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
|
import com.keylesspalace.tusky.entity.MastoList
|
|
import com.keylesspalace.tusky.fragment.TimelineFragment
|
|
import com.keylesspalace.tusky.util.*
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.Event.*
|
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.*
|
|
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
|
import com.mikepenz.iconics.IconicsDrawable
|
|
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
|
import com.uber.autodispose.autoDispose
|
|
import dagger.android.DispatchingAndroidInjector
|
|
import dagger.android.HasAndroidInjector
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
import kotlinx.android.synthetic.main.activity_lists.*
|
|
import kotlinx.android.synthetic.main.toolbar_basic.*
|
|
import javax.inject.Inject
|
|
|
|
/**
|
|
* Created by charlag on 1/4/18.
|
|
*/
|
|
|
|
class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|
|
|
companion object {
|
|
@JvmStatic
|
|
fun newIntent(context: Context): Intent {
|
|
return Intent(context, ListsActivity::class.java)
|
|
}
|
|
}
|
|
|
|
@Inject
|
|
lateinit var viewModelFactory: ViewModelFactory
|
|
|
|
@Inject
|
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
|
|
|
private lateinit var viewModel: ListsViewModel
|
|
private val adapter = ListsAdapter()
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_lists)
|
|
|
|
|
|
setSupportActionBar(toolbar)
|
|
supportActionBar?.apply {
|
|
title = getString(R.string.title_lists)
|
|
setDisplayHomeAsUpEnabled(true)
|
|
setDisplayShowHomeEnabled(true)
|
|
}
|
|
|
|
listsRecycler.adapter = adapter
|
|
listsRecycler.layoutManager = LinearLayoutManager(this)
|
|
listsRecycler.addItemDecoration(
|
|
DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
|
|
|
|
viewModel = viewModelFactory.create(ListsViewModel::class.java)
|
|
viewModel.state
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.autoDispose(from(this))
|
|
.subscribe(this::update)
|
|
viewModel.retryLoading()
|
|
|
|
addListButton.setOnClickListener {
|
|
showlistNameDialog(null)
|
|
}
|
|
|
|
viewModel.events.observeOn(AndroidSchedulers.mainThread())
|
|
.autoDispose(from(this))
|
|
.subscribe { event ->
|
|
@Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
|
|
when (event) {
|
|
CREATE_ERROR -> showMessage(R.string.error_create_list)
|
|
RENAME_ERROR -> showMessage(R.string.error_rename_list)
|
|
DELETE_ERROR -> showMessage(R.string.error_delete_list)
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
.setMargins(margin, margin, margin, 0)
|
|
|
|
val dialog = AlertDialog.Builder(this)
|
|
.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) { d, _ ->
|
|
d.dismiss()
|
|
}
|
|
.show()
|
|
|
|
val positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE)
|
|
editText.onTextChanged { s, _, _, _ ->
|
|
positiveButton.isEnabled = !s.isNullOrBlank()
|
|
}
|
|
editText.setText(list?.title)
|
|
editText.text?.let { editText.setSelection(it.length) }
|
|
}
|
|
|
|
|
|
private fun update(state: ListsViewModel.State) {
|
|
adapter.submitList(state.lists)
|
|
progressBar.visible(state.loadingState == LOADING)
|
|
when (state.loadingState) {
|
|
INITIAL, LOADING -> messageView.hide()
|
|
ERROR_NETWORK -> {
|
|
messageView.show()
|
|
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
|
viewModel.retryLoading()
|
|
}
|
|
}
|
|
ERROR_OTHER -> {
|
|
messageView.show()
|
|
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
|
viewModel.retryLoading()
|
|
}
|
|
}
|
|
LOADED ->
|
|
if (state.lists.isEmpty()) {
|
|
messageView.show()
|
|
messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
|
|
null)
|
|
} else {
|
|
messageView.hide()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showMessage(@StringRes messageId: Int) {
|
|
Snackbar.make(
|
|
listsRecycler, messageId, Snackbar.LENGTH_SHORT
|
|
).show()
|
|
|
|
}
|
|
|
|
private fun onListSelected(listId: String) {
|
|
startActivityWithSlideInAnimation(
|
|
ModalTimelineActivity.newIntent(this, TimelineFragment.Kind.LIST, listId))
|
|
}
|
|
|
|
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)
|
|
R.id.list_delete -> viewModel.deleteList(list.id)
|
|
else -> return@setOnMenuItemClickListener false
|
|
}
|
|
true
|
|
}
|
|
show()
|
|
}
|
|
}
|
|
|
|
override fun androidInjector() = dispatchingAndroidInjector
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
if (item.itemId == android.R.id.home) {
|
|
onBackPressed()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private object ListsDiffer : DiffUtil.ItemCallback<MastoList>() {
|
|
override fun areItemsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
|
|
return oldItem.id == newItem.id
|
|
}
|
|
|
|
override fun areContentsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
|
|
return oldItem == newItem
|
|
}
|
|
}
|
|
|
|
private inner class ListsAdapter
|
|
: ListAdapter<MastoList, ListsAdapter.ListViewHolder>(ListsDiffer) {
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
|
return LayoutInflater.from(parent.context).inflate(R.layout.item_list, parent, false)
|
|
.let(this::ListViewHolder)
|
|
.apply {
|
|
val context = nameTextView.context
|
|
val iconColor = ThemeUtils.getColor(context, android.R.attr.textColorTertiary)
|
|
val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).sizeDp(20).color(iconColor)
|
|
|
|
nameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
|
|
}
|
|
}
|
|
|
|
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
|
holder.nameTextView.text = getItem(position).title
|
|
}
|
|
|
|
private inner class ListViewHolder(view: View) : RecyclerView.ViewHolder(view),
|
|
View.OnClickListener {
|
|
val nameTextView: TextView = view.findViewById(R.id.list_name_textview)
|
|
val moreButton: ImageButton = view.findViewById(R.id.editListButton)
|
|
|
|
init {
|
|
view.setOnClickListener(this)
|
|
moreButton.setOnClickListener(this)
|
|
}
|
|
|
|
override fun onClick(v: View) {
|
|
if (v == itemView) {
|
|
onListSelected(getItem(adapterPosition).id)
|
|
} else {
|
|
onMore(getItem(adapterPosition), v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun onPickedDialogName(name: CharSequence, listId: String?) {
|
|
if (listId == null) {
|
|
viewModel.createNewList(name.toString())
|
|
} else {
|
|
viewModel.renameList(listId, name.toString())
|
|
}
|
|
}
|
|
} |