Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/activity/TrendsLocationSelectorActiv...

302 lines
11 KiB
Kotlin

package org.mariotaku.twidere.activity
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import androidx.fragment.app.DialogFragment
import androidx.collection.LongSparseArray
import androidx.appcompat.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseExpandableListAdapter
import android.widget.ExpandableListView
import android.widget.TextView
import kotlinx.android.synthetic.main.dialog_expandable_list.*
import nl.komponents.kovenant.combine.and
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.getTypedArray
import org.mariotaku.ktextension.set
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Location
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.extension.onShow
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import java.lang.ref.WeakReference
import java.text.Collator
import java.util.*
/**
* Created by mariotaku on 2017/2/2.
*/
class TrendsLocationSelectorActivity : BaseActivity() {
private val accountKey: UserKey?
get() = intent.getParcelableExtra<UserKey>(EXTRA_ACCOUNT_KEY)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val accountKey = this.accountKey ?: run {
finish()
return
}
if (savedInstanceState != null) return
val weakThis = WeakReference(this)
executeAfterFragmentResumed {
ProgressDialogFragment.show(it.supportFragmentManager, PROGRESS_FRAGMENT_TAG).isCancelable = false
} and task {
val activity = weakThis.get() ?: throw InterruptedException()
val twitter = MicroBlogAPIFactory.getInstance(activity, accountKey)
?: throw MicroBlogException("No account")
val map = LocationsMap(Locale.getDefault())
twitter.availableTrends.forEach { location -> map.put(location) }
return@task map.pack()
}.successUi { result ->
val activity = weakThis.get() ?: return@successUi
activity.executeAfterFragmentResumed {
val df = TrendsLocationDialogFragment()
df.arguments = Bundle {
this[EXTRA_DATA] = result
}
df.show(it.supportFragmentManager, "trends_location_selector")
}
}.failUi {
val activity = weakThis.get() ?: return@failUi
activity.finish()
}.alwaysUi {
val activity = weakThis.get() ?: return@alwaysUi
activity.executeAfterFragmentResumed {
val fm = it.supportFragmentManager
val df = fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG) as? DialogFragment
df?.dismiss()
}
}
}
class TrendsLocationDialogFragment : BaseDialogFragment() {
private val list: Array<LocationsMap.LocationsData> get() = arguments?.getTypedArray(EXTRA_DATA) ?: emptyArray()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val selectorBuilder = AlertDialog.Builder(requireContext())
selectorBuilder.setTitle(R.string.trends_location)
selectorBuilder.setView(R.layout.dialog_expandable_list)
selectorBuilder.setNegativeButton(android.R.string.cancel, null)
val dialog = selectorBuilder.create()
dialog.onShow {
it.applyTheme()
val listView = it.expandableList
val adapter = ExpandableTrendLocationsListAdapter(requireContext())
adapter.data = list
listView.setAdapter(adapter)
listView.setOnGroupClickListener(ExpandableListView.OnGroupClickListener { _, _, groupPosition, _ ->
val group = adapter.getGroup(groupPosition)
if (group.woeid.toLong() == WORLDWIDE) {
setActivityResult(group)
dismiss()
return@OnGroupClickListener true
}
return@OnGroupClickListener false
})
listView.setOnChildClickListener { _, _, groupPosition, childPosition, _ ->
val child = adapter.getChild(groupPosition, childPosition)
setActivityResult(child)
dismiss()
return@setOnChildClickListener true
}
}
dialog.show()
return dialog
}
private fun setActivityResult(location: Location) {
activity?.setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_LOCATION, location))
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.finish()
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
activity?.finish()
}
}
internal class ExpandableTrendLocationsListAdapter(context: Context) : BaseExpandableListAdapter() {
private val inflater = LayoutInflater.from(context)
var data: Array<LocationsMap.LocationsData>? = null
set(value) {
field = value
notifyDataSetChanged()
}
override fun getGroupCount(): Int {
return data?.size ?: 0
}
override fun getChildrenCount(groupPosition: Int): Int {
return data!![groupPosition].children.size
}
override fun getGroup(groupPosition: Int): Location {
return data!![groupPosition].root
}
override fun getChild(groupPosition: Int, childPosition: Int): Location {
return data!![groupPosition].children[childPosition]
}
override fun getGroupId(groupPosition: Int): Long {
return getGroup(groupPosition).woeid.toLong()
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return getChild(groupPosition, childPosition).woeid.toLong()
}
override fun hasStableIds(): Boolean {
return true
}
override fun getGroupView(groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup): View {
val view: View = convertView ?: inflater.inflate(android.R.layout.simple_expandable_list_item_1, parent, false)
view.findViewById<TextView>(android.R.id.text1).text = getGroup(groupPosition).name
return view
}
override fun getChildView(groupPosition: Int, childPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup): View {
val view: View =
convertView ?: inflater.inflate(android.R.layout.simple_list_item_1, parent, false)
val location = getChild(groupPosition, childPosition)
val text1 = view.findViewById<TextView>(android.R.id.text1)
if (location.parentId == WORLDWIDE) {
text1.setText(R.string.location_countrywide)
} else {
text1.text = location.name
}
return view
}
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return true
}
}
private class LocationComparator(private val collator: Collator) : Comparator<Location> {
private fun isCountryOrWorldwide(location: Location): Boolean {
val parentId = location.parentId
return parentId == EMPTY || parentId == WORLDWIDE
}
override fun compare(lhs: Location, rhs: Location): Int {
if (isCountryOrWorldwide(lhs)) return Integer.MIN_VALUE
if (isCountryOrWorldwide(rhs)) return Integer.MAX_VALUE
return collator.compare(lhs.name, rhs.name)
}
}
internal class LocationsMap(locale: Locale) {
val map = LongSparseArray<MutableList<Location>>()
val parents = LongSparseArray<Location>()
private val comparator = LocationComparator(Collator.getInstance(locale))
fun put(location: Location) {
val parentId = location.parentId
if (parentId == EMPTY || parentId == WORLDWIDE) {
putParent(location)
} else {
putChild(parentId, location)
}
}
fun putParent(location: Location) {
val woeid = location.woeid.toLong()
parents.put(woeid, location)
val list = getList(woeid)
// Don't add child for 'worldwide'
if (woeid != WORLDWIDE) {
addToList(list, location)
}
}
fun putChild(parentId: Long, location: Location) {
addToList(getList(parentId), location)
}
fun getList(parentId: Long): MutableList<Location> {
return map.get(parentId) ?: run {
val l = ArrayList<Location>()
map.put(parentId, l)
return@run l
}
}
fun addToList(list: MutableList<Location>, location: Location) {
val loc = Collections.binarySearch(list, location, comparator)
if (loc < 0) {
list.add(-(loc + 1), location)
}
}
fun pack(): Array<LocationsData> {
return (0 until map.size()).mapNotNull { i ->
val parent = parents.get(map.keyAt(i)) ?: return@mapNotNull null
return@mapNotNull LocationsData(parent, map.valueAt(i).toTypedArray())
}.toTypedArray()
}
data class LocationsData(val root: Location, val children: Array<Location>) : Parcelable {
override fun describeContents(): Int = 0
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(root, flags)
dest.writeTypedArray(children, flags)
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<LocationsData> {
override fun createFromParcel(source: Parcel): LocationsData {
val root = source.readParcelable<Location>(Location::class.java.classLoader)!!
val children = source.createTypedArray(Location.CREATOR)!!
return LocationsData(root, children)
}
override fun newArray(size: Int): Array<out LocationsData?> {
return arrayOfNulls<LocationsData>(size)
}
}
}
}
}
companion object {
private const val PROGRESS_FRAGMENT_TAG = "load_location_progress"
private const val EMPTY: Long = 0
private const val WORLDWIDE: Long = 1
}
}