/* * Twidere - Twitter client for Android * * Copyright (C) 2012-2014 Mariotaku Lee * * 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 . */ package org.mariotaku.twidere.fragment import android.app.Activity import android.app.Dialog import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter.CreateNdefMessageCallback import android.os.Bundle import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager.LoaderCallbacks import androidx.loader.content.FixedAsyncTaskLoader import androidx.loader.content.Loader import androidx.core.content.pm.ShortcutManagerCompat import androidx.appcompat.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 com.squareup.otto.Subscribe import org.mariotaku.kpreferences.get import org.mariotaku.ktextension.setItemAvailability import org.mariotaku.microblog.library.MicroBlogException import org.mariotaku.microblog.library.twitter.model.UserList import org.mariotaku.twidere.Constants.* import org.mariotaku.twidere.R import org.mariotaku.twidere.activity.AccountSelectorActivity import org.mariotaku.twidere.activity.UserSelectorActivity import org.mariotaku.twidere.adapter.SupportTabsAdapter import org.mariotaku.twidere.app.TwidereApplication import org.mariotaku.twidere.constant.newDocumentApiKey import org.mariotaku.twidere.extension.applyTheme import org.mariotaku.twidere.extension.model.api.microblog.toParcelable import org.mariotaku.twidere.extension.neutral import org.mariotaku.twidere.extension.onShow import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowInsetsCallback import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback import org.mariotaku.twidere.fragment.statuses.UserListTimelineFragment import org.mariotaku.twidere.fragment.users.UserListMembersFragment import org.mariotaku.twidere.fragment.users.UserListSubscribersFragment import org.mariotaku.twidere.model.ParcelableUser import org.mariotaku.twidere.model.ParcelableUserList import org.mariotaku.twidere.model.SingleResponse import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.event.UserListSubscriptionEvent import org.mariotaku.twidere.model.event.UserListUpdatedEvent import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.shortcut.ShortcutCreator class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCallbacks>, SystemWindowInsetsCallback, SupportFragmentCallback { private var userListLoaderInitialized: Boolean = false var userList: ParcelableUserList? = null private set fun displayUserList(userList: ParcelableUserList?) { val activity = activity ?: return LoaderManager.getInstance(this).destroyLoader(0) this.userList = userList if (userList != null) { activity.title = userList.name } else { activity.setTitle(R.string.title_user_list) } activity.invalidateOptionsMenu() } fun getUserListInfo(omitIntentExtra: Boolean) { val lm = LoaderManager.getInstance(this) lm.destroyLoader(0) val args = Bundle(arguments) args.putBoolean(EXTRA_OMIT_INTENT_EXTRA, omitIntentExtra) if (!userListLoaderInitialized) { lm.initLoader(0, args, this) userListLoaderInitialized = true } 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 if (resultCode != Activity.RESULT_OK || !data!!.hasExtra(EXTRA_USER) || userList == null) return val user = data.getParcelableExtra(EXTRA_USER) ?: return 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(EXTRA_ACCOUNT_KEY) activity?.let { IntentUtils.openUserListDetails(it, 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) if (activity != null) { 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 ?: return val tabArgs = Bundle() if (args.containsKey(EXTRA_USER_LIST)) { val userList = args.getParcelable(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)) } 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)) adapter.add(cls = UserListSubscribersFragment::class.java, args = tabArgs, name = getString(R.string.title_userlist_subscribers)) } override fun onStart() { super.onStart() bus.register(this) } override fun onStop() { bus.unregister(this) super.onStop() } override fun onDestroyView() { userList = null LoaderManager.getInstance(this).destroyLoader(0) super.onDestroyView() } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.menu_user_list, menu) } override fun onPrepareOptionsMenu(menu: Menu) { val userList = this.userList menu.setItemAvailability(R.id.info, userList != null) menu.removeGroup(MENU_GROUP_USER_LIST_EXTENSION) if (userList != null) { val isMyList = userList.user_key == userList.account_key val isFollowing = userList.is_following menu.setItemAvailability(R.id.edit, isMyList) menu.setItemAvailability(R.id.follow, !isMyList) menu.setItemAvailability(R.id.add, isMyList) menu.setItemAvailability(R.id.delete, isMyList) val followItem = menu.findItem(R.id.follow) if (isFollowing) { followItem.setIcon(R.drawable.ic_action_cancel) followItem.setTitle(R.string.action_unsubscribe) } else { followItem.setIcon(R.drawable.ic_action_add) followItem.setTitle(R.string.action_subscribe) } val extensionsIntent = Intent(INTENT_ACTION_EXTENSION_OPEN_USER_LIST) extensionsIntent.setExtrasClassLoader(TwidereApplication::class.java.classLoader) extensionsIntent.putExtra(EXTRA_USER_LIST, userList) activity?.let { MenuUtils.addIntentToMenu(it, menu, extensionsIntent, MENU_GROUP_USER_LIST_EXTENSION) menu.setItemAvailability(R.id.add_to_home_screen_submenu, ShortcutManagerCompat.isRequestPinShortcutSupported(it)) } } else { menu.setItemAvailability(R.id.edit, false) menu.setItemAvailability(R.id.follow, false) menu.setItemAvailability(R.id.add, false) menu.setItemAvailability(R.id.delete, false) menu.setItemAvailability(R.id.add_to_home_screen_submenu, false) } } override fun onOptionsItemSelected(item: MenuItem): Boolean { val twitter = twitterWrapper val userList = userList ?: return false val activity = activity ?: return false val fragmentManager = parentFragmentManager when (item.itemId) { R.id.add -> { if (userList.user_key != userList.account_key) return false val intent = Intent(INTENT_ACTION_SELECT_USER) intent.setClass(activity, UserSelectorActivity::class.java) 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) } R.id.info -> { val df = UserListDetailsDialogFragment() df.arguments = Bundle().apply { putParcelable(EXTRA_USER_LIST, userList) } df.show(childFragmentManager, "user_list_details") } R.id.add_statuses_to_home_screen -> { ShortcutCreator.performCreation(this) { ShortcutCreator.userListTimeline(activity, userList.account_key, userList) } } 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) } R.id.profileImage -> { val userList = this.userList ?: return activity?.let { IntentUtils.openUserProfile(it, userList.account_key, userList.user_key, userList.user_screen_name, null, preferences[newDocumentApiKey], null) } } } } override fun onCreateLoader(id: Int, args: Bundle?): Loader> { val accountKey = args!!.getParcelable(EXTRA_ACCOUNT_KEY) val userKey = args.getParcelable(EXTRA_USER_KEY) 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(requireActivity(), omitIntentExtra, arguments, accountKey, listId, listName, userKey, screenName) } override fun onLoadFinished(loader: Loader>, data: SingleResponse?) { 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>) { } @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) } } internal class ParcelableUserListLoader( context: Context, private val omitIntentExtra: Boolean, private val extras: Bundle?, private val accountKey: UserKey?, private val listId: String?, private val listName: String?, private val userKey: UserKey?, private val screenName: String? ) : FixedAsyncTaskLoader>(context) { override fun loadInBackground(): SingleResponse { if (!omitIntentExtra && extras != null) { val cache = extras.getParcelable(EXTRA_USER_LIST) if (cache != null) return SingleResponse(cache) } try { if (accountKey == null) throw MicroBlogException("No account") val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: throw MicroBlogException("No account") val list: UserList list = when { listId != null -> { twitter.showUserList(listId) } listName != null && userKey != null -> { twitter.showUserList(listName, userKey.id) } listName != null && screenName != null -> { twitter.showUserListByScrenName(listName, screenName) } else -> { return SingleResponse(MicroBlogException("Invalid argument")) } } return SingleResponse(list.toParcelable(accountKey)) } catch (e: MicroBlogException) { return SingleResponse(e) } } override fun onStartLoading() { forceLoad() } } class UserListDetailsDialogFragment : BaseDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val userList = requireArguments().getParcelable(EXTRA_USER_LIST)!! val builder = AlertDialog.Builder(requireContext()) builder.setTitle(userList.name) builder.setMessage(userList.description) builder.setPositiveButton(android.R.string.ok, null) builder.neutral(R.string.action_list_creator) { startActivity(IntentUtils.userProfile(userList.account_key, userList.user_key, userList.user_screen_name)) } val dialog = builder.create() dialog.onShow { it.applyTheme() } return dialog } } }