Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserListFragment.kt

453 lines
19 KiB
Kotlin
Raw Normal View History

2016-07-02 05:54:53 +02:00
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.nfc.NdefMessage
import android.nfc.NdefRecord
import android.nfc.NfcAdapter.CreateNdefMessageCallback
import android.os.Bundle
import android.support.v4.app.LoaderManager.LoaderCallbacks
2017-02-07 16:36:29 +01:00
import android.support.v4.content.FixedAsyncTaskLoader
2016-07-02 05:54:53 +02:00
import android.support.v4.content.Loader
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.OnClickListener
import android.widget.CheckBox
import com.rengwuxian.materialedittext.MaterialEditText
import com.squareup.otto.Subscribe
2017-04-07 13:05:02 +02:00
import org.mariotaku.kpreferences.get
2017-04-12 10:17:20 +02:00
import org.mariotaku.ktextension.setItemAvailability
2016-07-02 05:54:53 +02:00
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.UserList
import org.mariotaku.microblog.library.twitter.model.UserListUpdate
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.AccountSelectorActivity
2017-01-29 14:34:22 +01:00
import org.mariotaku.twidere.activity.UserSelectorActivity
2016-07-02 05:54:53 +02:00
import org.mariotaku.twidere.adapter.SupportTabsAdapter
2017-04-21 11:23:55 +02:00
import org.mariotaku.twidere.app.TwidereApplication
2017-04-07 13:05:02 +02:00
import org.mariotaku.twidere.constant.newDocumentApiKey
2017-02-05 14:42:20 +01:00
import org.mariotaku.twidere.extension.applyTheme
2017-04-21 12:01:54 +02:00
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
2016-07-02 05:54:53 +02:00
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
2017-04-21 14:24:15 +02:00
import org.mariotaku.twidere.fragment.statuses.UserListTimelineFragment
2017-04-21 11:23:55 +02:00
import org.mariotaku.twidere.fragment.users.UserListMembersFragment
import org.mariotaku.twidere.fragment.users.UserListSubscribersFragment
2016-07-02 05:54:53 +02:00
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.ParcelableUserList
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
2017-02-09 09:00:12 +01:00
import org.mariotaku.twidere.model.event.UserListSubscriptionEvent
import org.mariotaku.twidere.model.event.UserListUpdatedEvent
2016-07-02 05:54:53 +02:00
import org.mariotaku.twidere.text.validator.UserListNameValidator
import org.mariotaku.twidere.util.*
2017-03-20 14:46:18 +01:00
class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener,
LoaderCallbacks<SingleResponse<ParcelableUserList>>, SystemWindowsInsetsCallback,
SupportFragmentCallback {
2016-07-02 05:54:53 +02:00
2016-07-08 07:24:39 +02:00
private var userListLoaderInitialized: Boolean = false
2016-07-02 05:54:53 +02:00
var userList: ParcelableUserList? = null
private set
fun displayUserList(userList: ParcelableUserList?) {
val activity = activity ?: return
loaderManager.destroyLoader(0)
this.userList = userList
if (userList != null) {
activity.title = userList.name
} else {
2017-04-17 15:10:14 +02:00
activity.setTitle(R.string.title_user_list)
2016-07-02 05:54:53 +02:00
}
2016-11-30 08:18:43 +01:00
activity.invalidateOptionsMenu()
2016-07-02 05:54:53 +02:00
}
fun getUserListInfo(omitIntentExtra: Boolean) {
val lm = loaderManager
lm.destroyLoader(0)
val args = Bundle(arguments)
args.putBoolean(EXTRA_OMIT_INTENT_EXTRA, omitIntentExtra)
2016-07-08 07:24:39 +02:00
if (!userListLoaderInitialized) {
2016-07-02 05:54:53 +02:00
lm.initLoader(0, args, this)
2016-07-08 07:24:39 +02:00
userListLoaderInitialized = true
2016-07-02 05:54:53 +02:00
} else {
lm.restartLoader(0, args, this)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val twitter = twitterWrapper
when (requestCode) {
REQUEST_SELECT_USER -> {
val userList = this.userList
2016-07-08 07:24:39 +02:00
if (resultCode != Activity.RESULT_OK || !data!!.hasExtra(EXTRA_USER) || userList == null)
2016-07-02 05:54:53 +02:00
return
val user = data.getParcelableExtra<ParcelableUser>(EXTRA_USER)
twitter.addUserListMembersAsync(userList.account_key, userList.id, user)
return
}
REQUEST_SELECT_ACCOUNT -> {
if (resultCode == Activity.RESULT_OK) {
if (data == null || !data.hasExtra(EXTRA_ID)) return
val userList = this.userList
val accountKey = data.getParcelableExtra<UserKey>(EXTRA_ACCOUNT_KEY)
IntentUtils.openUserListDetails(activity, accountKey, userList!!.id,
userList.user_key, userList.user_screen_name, userList.name)
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val activity = activity
setHasOptionsMenu(true)
Utils.setNdefPushMessageCallback(activity, CreateNdefMessageCallback {
val userList = userList ?: return@CreateNdefMessageCallback null
NdefMessage(arrayOf(NdefRecord.createUri(LinkCreator.getTwitterUserListLink(userList.user_screen_name, userList.name))))
})
getUserListInfo(false)
}
override fun addTabs(adapter: SupportTabsAdapter) {
val args = arguments
val tabArgs = Bundle()
if (args.containsKey(EXTRA_USER_LIST)) {
val userList = args.getParcelable<ParcelableUserList>(EXTRA_USER_LIST)!!
tabArgs.putParcelable(EXTRA_ACCOUNT_KEY, userList.account_key)
tabArgs.putParcelable(EXTRA_USER_KEY, userList.user_key)
tabArgs.putString(EXTRA_SCREEN_NAME, userList.user_screen_name)
tabArgs.putString(EXTRA_LIST_ID, userList.id)
tabArgs.putString(EXTRA_LIST_NAME, userList.name)
} else {
tabArgs.putParcelable(EXTRA_ACCOUNT_KEY, args.getParcelable(EXTRA_ACCOUNT_KEY))
tabArgs.putParcelable(EXTRA_USER_KEY, args.getParcelable(EXTRA_USER_KEY))
tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME))
tabArgs.putString(EXTRA_LIST_ID, args.getString(EXTRA_LIST_ID))
tabArgs.putString(EXTRA_LIST_NAME, args.getString(EXTRA_LIST_NAME))
}
2017-03-27 05:08:09 +02:00
adapter.add(cls = UserListTimelineFragment::class.java, args = tabArgs, name = getString(R.string.title_statuses))
adapter.add(cls = UserListMembersFragment::class.java, args = tabArgs, name = getString(R.string.members))
2017-05-21 10:51:03 +02:00
adapter.add(cls = UserListSubscribersFragment::class.java, args = tabArgs, name = getString(R.string.title_userlist_subscribers))
2016-07-02 05:54:53 +02:00
}
override fun onStart() {
super.onStart()
bus.register(this)
}
override fun onStop() {
bus.unregister(this)
super.onStop()
}
override fun onDestroyView() {
userList = null
loaderManager.destroyLoader(0)
super.onDestroyView()
}
2017-04-12 10:17:20 +02:00
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_user_list, menu)
2016-07-02 05:54:53 +02:00
}
2017-04-12 10:17:20 +02:00
override fun onPrepareOptionsMenu(menu: Menu) {
2016-07-02 05:54:53 +02:00
val userList = this.userList
2017-04-12 10:17:20 +02:00
menu.setItemAvailability(R.id.info, userList != null)
menu.removeGroup(MENU_GROUP_USER_LIST_EXTENSION)
2016-07-02 05:54:53 +02:00
if (userList != null) {
val isMyList = userList.user_key == userList.account_key
val isFollowing = userList.is_following
2017-04-12 10:17:20 +02:00
menu.setItemAvailability(R.id.edit, isMyList)
menu.setItemAvailability(R.id.follow, !isMyList)
menu.setItemAvailability(R.id.add, isMyList)
menu.setItemAvailability(R.id.delete, isMyList)
2016-07-02 05:54:53 +02:00
val followItem = menu.findItem(R.id.follow)
if (isFollowing) {
followItem.setIcon(R.drawable.ic_action_cancel)
2017-01-26 14:28:43 +01:00
followItem.setTitle(R.string.action_unsubscribe)
2016-07-02 05:54:53 +02:00
} else {
followItem.setIcon(R.drawable.ic_action_add)
2017-01-26 14:28:43 +01:00
followItem.setTitle(R.string.action_subscribe)
2016-07-02 05:54:53 +02:00
}
val extensionsIntent = Intent(INTENT_ACTION_EXTENSION_OPEN_USER_LIST)
2017-04-21 11:23:55 +02:00
extensionsIntent.setExtrasClassLoader(TwidereApplication::class.java.classLoader)
2016-07-02 05:54:53 +02:00
extensionsIntent.putExtra(EXTRA_USER_LIST, userList)
MenuUtils.addIntentToMenu(activity, menu, extensionsIntent, MENU_GROUP_USER_LIST_EXTENSION)
} else {
2017-04-12 10:17:20 +02:00
menu.setItemAvailability(R.id.edit, false)
menu.setItemAvailability(R.id.follow, false)
menu.setItemAvailability(R.id.add, false)
menu.setItemAvailability(R.id.delete, false)
2016-07-02 05:54:53 +02:00
}
}
2017-04-12 10:17:20 +02:00
override fun onOptionsItemSelected(item: MenuItem): Boolean {
2016-07-02 05:54:53 +02:00
val twitter = twitterWrapper
val userList = userList ?: return false
2017-04-12 10:17:20 +02:00
when (item.itemId) {
2016-07-02 05:54:53 +02:00
R.id.add -> {
if (userList.user_key != userList.account_key) return false
val intent = Intent(INTENT_ACTION_SELECT_USER)
2017-01-29 14:34:22 +01:00
intent.setClass(activity, UserSelectorActivity::class.java)
2016-07-02 05:54:53 +02:00
intent.putExtra(EXTRA_ACCOUNT_KEY, userList.account_key)
startActivityForResult(intent, REQUEST_SELECT_USER)
}
R.id.delete -> {
if (userList.user_key != userList.account_key) return false
DestroyUserListDialogFragment.show(fragmentManager, userList)
}
R.id.edit -> {
val args = Bundle()
args.putParcelable(EXTRA_ACCOUNT_KEY, userList.account_key)
args.putString(EXTRA_LIST_NAME, userList.name)
args.putString(EXTRA_DESCRIPTION, userList.description)
args.putBoolean(EXTRA_IS_PUBLIC, userList.is_public)
args.putString(EXTRA_LIST_ID, userList.id)
val f = EditUserListDialogFragment()
f.arguments = args
f.show(fragmentManager, "edit_user_list_details")
return true
}
R.id.follow -> {
if (userList.is_following) {
DestroyUserListSubscriptionDialogFragment.show(fragmentManager, userList)
} else {
twitter.createUserListSubscriptionAsync(userList.account_key, userList.id)
}
return true
}
R.id.open_with_account -> {
val intent = Intent(INTENT_ACTION_SELECT_ACCOUNT)
intent.setClass(activity, AccountSelectorActivity::class.java)
intent.putExtra(EXTRA_SINGLE_SELECTION, true)
startActivityForResult(intent, REQUEST_SELECT_ACCOUNT)
}
2016-07-08 07:24:39 +02:00
R.id.info -> {
val df = UserListDetailsDialogFragment()
df.arguments = Bundle()
df.arguments.putParcelable(EXTRA_USER_LIST, userList)
df.show(childFragmentManager, "user_list_details")
}
2016-07-02 05:54:53 +02:00
else -> {
if (item.intent != null) {
try {
startActivity(item.intent)
} catch (e: ActivityNotFoundException) {
Log.w(LOGTAG, e)
return false
}
}
}
}
return true
}
override fun onClick(view: View) {
when (view.id) {
R.id.errorContainer -> {
getUserListInfo(true)
}
2016-07-02 06:05:23 +02:00
R.id.profileImage -> {
2016-07-02 05:54:53 +02:00
val userList = this.userList ?: return
2017-04-07 13:05:02 +02:00
IntentUtils.openUserProfile(activity, userList.account_key, userList.user_key,
userList.user_screen_name, null, preferences[newDocumentApiKey], null, null)
2016-07-02 05:54:53 +02:00
}
}
}
override fun onCreateLoader(id: Int, args: Bundle): Loader<SingleResponse<ParcelableUserList>> {
2017-04-12 14:58:08 +02:00
val accountKey = args.getParcelable<UserKey?>(EXTRA_ACCOUNT_KEY)
val userKey = args.getParcelable<UserKey?>(EXTRA_USER_KEY)
2016-07-02 05:54:53 +02:00
val listId = args.getString(EXTRA_LIST_ID)
val listName = args.getString(EXTRA_LIST_NAME)
val screenName = args.getString(EXTRA_SCREEN_NAME)
val omitIntentExtra = args.getBoolean(EXTRA_OMIT_INTENT_EXTRA, true)
return ParcelableUserListLoader(activity, omitIntentExtra, arguments, accountKey, listId,
listName, userKey, screenName)
}
override fun onLoadFinished(loader: Loader<SingleResponse<ParcelableUserList>>,
2017-04-07 13:05:02 +02:00
data: SingleResponse<ParcelableUserList>?) {
2016-07-02 05:54:53 +02:00
if (data == null) return
if (activity == null) return
if (data.hasData()) {
val list = data.data
displayUserList(list)
} else if (data.hasException()) {
}
}
override fun onLoaderReset(loader: Loader<SingleResponse<ParcelableUserList>>) {
}
@Subscribe
fun onUserListUpdated(event: UserListUpdatedEvent) {
if (userList == null) return
if (TextUtils.equals(event.userList.id, userList!!.id)) {
getUserListInfo(true)
}
}
@Subscribe
fun onUserListSubscriptionChanged(event: UserListSubscriptionEvent) {
if (userList == null) return
if (TextUtils.equals(event.userList.id, userList!!.id)) {
getUserListInfo(true)
}
}
class EditUserListDialogFragment : BaseDialogFragment(), DialogInterface.OnClickListener {
2017-02-10 16:38:59 +01:00
private val accountKey by lazy { arguments.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY) }
private val listId: String by lazy { arguments.getString(EXTRA_LIST_ID) }
2016-07-02 05:54:53 +02:00
override fun onClick(dialog: DialogInterface, which: Int) {
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val alertDialog = dialog as AlertDialog
2017-02-10 16:38:59 +01:00
val editName = alertDialog.findViewById(R.id.name) as MaterialEditText
val editDescription = alertDialog.findViewById(R.id.description) as MaterialEditText
val editIsPublic = alertDialog.findViewById(R.id.is_public) as CheckBox
val name = ParseUtils.parseString(editName.text)
val description = ParseUtils.parseString(editDescription.text)
val isPublic = editIsPublic.isChecked
2016-07-02 05:54:53 +02:00
if (TextUtils.isEmpty(name)) return
val update = UserListUpdate()
update.setMode(if (isPublic) UserList.Mode.PUBLIC else UserList.Mode.PRIVATE)
update.setName(name)
update.setDescription(description)
2017-02-10 16:38:59 +01:00
twitterWrapper.updateUserListDetails(accountKey, listId, update)
2016-07-02 05:54:53 +02:00
}
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setView(R.layout.dialog_user_list_detail_editor)
2017-04-17 15:10:14 +02:00
builder.setTitle(R.string.title_user_list)
2016-07-02 05:54:53 +02:00
builder.setPositiveButton(android.R.string.ok, this)
builder.setNegativeButton(android.R.string.cancel, this)
val dialog = builder.create()
dialog.setOnShowListener { dialog ->
2017-02-05 14:42:20 +01:00
dialog as AlertDialog
dialog.applyTheme()
2017-02-10 16:38:59 +01:00
val editName = dialog.findViewById(R.id.name) as MaterialEditText
val editDescription = dialog.findViewById(R.id.description) as MaterialEditText
val editPublic = dialog.findViewById(R.id.is_public) as CheckBox
editName.addValidator(UserListNameValidator(getString(R.string.invalid_list_name)))
if (savedInstanceState == null) {
editName.setText(arguments.getString(EXTRA_LIST_NAME))
editDescription.setText(arguments.getString(EXTRA_DESCRIPTION))
editPublic.isChecked = arguments.getBoolean(EXTRA_IS_PUBLIC, true)
2016-07-02 05:54:53 +02:00
}
}
return dialog
}
}
internal class ParcelableUserListLoader(
context: Context,
private val omitIntentExtra: Boolean,
private val extras: Bundle?,
2017-04-12 14:58:08 +02:00
private val accountKey: UserKey?,
2016-07-02 05:54:53 +02:00
private val listId: String?,
2016-07-02 06:11:07 +02:00
private val listName: String?,
2016-07-02 05:54:53 +02:00
private val userKey: UserKey?,
private val screenName: String?
2017-02-07 16:36:29 +01:00
) : FixedAsyncTaskLoader<SingleResponse<ParcelableUserList>>(context) {
2016-07-02 05:54:53 +02:00
override fun loadInBackground(): SingleResponse<ParcelableUserList> {
if (!omitIntentExtra && extras != null) {
val cache = extras.getParcelable<ParcelableUserList>(EXTRA_USER_LIST)
2017-02-10 16:38:59 +01:00
if (cache != null) return SingleResponse(cache)
2016-07-02 05:54:53 +02:00
}
try {
2017-04-12 14:58:08 +02:00
if (accountKey == null) throw MicroBlogException("No account")
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey)
?: throw MicroBlogException("No account")
2016-07-02 05:54:53 +02:00
val list: UserList
2016-07-02 06:11:07 +02:00
when {
listId != null -> {
list = twitter.showUserList(listId)
}
listName != null && userKey != null -> {
list = twitter.showUserList(listName, userKey.id)
}
listName != null && screenName != null -> {
list = twitter.showUserListByScrenName(listName, screenName)
}
else -> {
2017-02-10 16:38:59 +01:00
return SingleResponse(MicroBlogException("Invalid argument"))
2016-07-02 06:11:07 +02:00
}
}
2017-04-21 12:01:54 +02:00
return SingleResponse(list.toParcelable(accountKey))
2016-07-02 05:54:53 +02:00
} catch (e: MicroBlogException) {
2017-02-10 16:38:59 +01:00
return SingleResponse(e)
2016-07-02 05:54:53 +02:00
}
}
override fun onStartLoading() {
2016-07-02 05:54:53 +02:00
forceLoad()
}
}
2016-07-08 07:24:39 +02:00
class UserListDetailsDialogFragment : BaseDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val userList = arguments.getParcelable<ParcelableUserList>(EXTRA_USER_LIST)
val builder = AlertDialog.Builder(context)
builder.setTitle(userList.name)
builder.setMessage(userList.description)
builder.setPositiveButton(android.R.string.ok, null)
2017-02-05 14:42:20 +01:00
val dialog = builder.create()
dialog.setOnShowListener {
it as AlertDialog
it.applyTheme()
}
return dialog
2016-07-08 07:24:39 +02:00
}
}
2016-07-02 05:54:53 +02:00
}