Merge pull request #346 from pavelpoley/task/sort-custom

Sort by custom order
This commit is contained in:
Tibor Kaputa 2022-05-17 10:06:10 +02:00 committed by GitHub
commit 6bf681903c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 207 additions and 21 deletions

View File

@ -31,6 +31,7 @@ import com.simplemobiletools.dialer.R
import com.simplemobiletools.dialer.adapters.ViewPagerAdapter
import com.simplemobiletools.dialer.dialogs.ChangeSortingDialog
import com.simplemobiletools.dialer.extensions.config
import com.simplemobiletools.dialer.fragments.FavoritesFragment
import com.simplemobiletools.dialer.fragments.MyViewPagerFragment
import com.simplemobiletools.dialer.helpers.OPEN_DIAL_PAD_AT_LAUNCH
import com.simplemobiletools.dialer.helpers.RecentsHelper
@ -141,7 +142,7 @@ class MainActivity : SimpleActivity() {
R.id.clear_call_history -> clearCallHistory()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
R.id.sort -> showSortingDialog()
R.id.sort -> showSortingDialog(showCustomSorting = getCurrentFragment() is FavoritesFragment)
else -> return super.onOptionsItemSelected(item)
}
return true
@ -476,15 +477,15 @@ class MainActivity : SimpleActivity() {
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
}
private fun showSortingDialog() {
ChangeSortingDialog(this) {
private fun showSortingDialog(showCustomSorting: Boolean) {
ChangeSortingDialog(this, showCustomSorting) {
favorites_fragment?.refreshItems {
if (isSearchOpen){
if (isSearchOpen) {
getCurrentFragment()?.onSearchQueryChanged(searchQuery)
}
}
contacts_fragment?.refreshItems {
if (isSearchOpen){
if (isSearchOpen) {
getCurrentFragment()?.onSearchQueryChanged(searchQuery)
}
}

View File

@ -8,11 +8,14 @@ import android.net.Uri
import android.text.TextUtils
import android.util.TypedValue
import android.view.Menu
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
@ -21,6 +24,9 @@ import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.isOreoPlus
import com.simplemobiletools.commons.interfaces.ItemMoveCallback
import com.simplemobiletools.commons.interfaces.ItemTouchHelperContract
import com.simplemobiletools.commons.interfaces.StartReorderDragListener
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.dialer.R
@ -30,18 +36,38 @@ import com.simplemobiletools.dialer.extensions.callContactWithSim
import com.simplemobiletools.dialer.extensions.config
import com.simplemobiletools.dialer.extensions.startContactDetailsIntent
import com.simplemobiletools.dialer.interfaces.RefreshItemsListener
import java.util.*
class ContactsAdapter(
activity: SimpleActivity, var contacts: ArrayList<SimpleContact>, recyclerView: MyRecyclerView, val refreshItemsListener: RefreshItemsListener? = null,
highlightText: String = "", val showDeleteButton: Boolean = true, itemClick: (Any) -> Unit
) :
MyRecyclerViewAdapter(activity, recyclerView, itemClick) {
activity: SimpleActivity,
var contacts: ArrayList<SimpleContact>,
recyclerView: MyRecyclerView,
val refreshItemsListener: RefreshItemsListener? = null,
highlightText: String = "",
val showDeleteButton: Boolean = true,
private val enableDrag: Boolean = false,
itemClick: (Any) -> Unit
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick), ItemTouchHelperContract {
private var textToHighlight = highlightText
private var fontSize = activity.getTextSize()
private var touchHelper: ItemTouchHelper? = null
private var startReorderDragListener: StartReorderDragListener? = null
var onDragEndListener: (() -> Unit)? = null
init {
setupDragListener(true)
if (enableDrag) {
touchHelper = ItemTouchHelper(ItemMoveCallback(this))
touchHelper!!.attachToRecyclerView(recyclerView)
startReorderDragListener = object : StartReorderDragListener {
override fun requestDrag(viewHolder: RecyclerView.ViewHolder) {
touchHelper?.startDrag(viewHolder)
}
}
}
}
override fun getActionMenuId() = R.menu.cab_contacts
@ -87,16 +113,20 @@ class ContactsAdapter(
override fun getItemKeyPosition(key: Int) = contacts.indexOfFirst { it.rawId == key }
override fun onActionModeCreated() {}
override fun onActionModeCreated() {
notifyDataSetChanged()
}
override fun onActionModeDestroyed() {}
override fun onActionModeDestroyed() {
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_contact_without_number, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contact = contacts[position]
holder.bindView(contact, true, true) { itemView, layoutPosition ->
setupView(itemView, contact)
setupView(itemView, contact, holder)
}
bindViewHolder(holder)
}
@ -216,7 +246,7 @@ class ContactsAdapter(
}
}
private fun setupView(view: View, contact: SimpleContact) {
private fun setupView(view: View, contact: SimpleContact, holder: ViewHolder) {
view.apply {
findViewById<FrameLayout>(R.id.item_contact_frame).isSelected = selectedKeys.contains(contact.rawId)
findViewById<TextView>(R.id.item_contact_name).apply {
@ -230,7 +260,25 @@ class ContactsAdapter(
contact.name.highlightTextFromNumbers(textToHighlight, properPrimaryColor)
}
}
}
val dragIcon = findViewById<ImageView>(R.id.drag_handle_icon)
if (enableDrag && textToHighlight.isEmpty()) {
dragIcon.apply {
beVisibleIf(selectedKeys.isNotEmpty())
applyColorFilter(textColor)
setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
startReorderDragListener?.requestDrag(holder)
}
false
}
}
} else {
dragIcon.apply {
beGone()
setOnTouchListener(null)
}
}
if (!activity.isDestroyed) {
@ -238,4 +286,26 @@ class ContactsAdapter(
}
}
}
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
activity.config.isCustomOrderSelected = true
if (fromPosition < toPosition) {
for (i in fromPosition until toPosition) {
Collections.swap(contacts, i, i + 1)
}
} else {
for (i in fromPosition downTo toPosition + 1) {
Collections.swap(contacts, i, i - 1)
}
}
notifyItemMoved(fromPosition, toPosition)
}
override fun onRowSelected(myViewHolder: ViewHolder?) {}
override fun onRowClear(myViewHolder: ViewHolder?) {
onDragEndListener?.invoke()
}
}

View File

@ -2,14 +2,22 @@ package com.simplemobiletools.dialer.dialogs
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beGoneIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.helpers.SORT_BY_CUSTOM
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_CREATED
import com.simplemobiletools.commons.helpers.SORT_BY_FULL_NAME
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.dialer.R
import com.simplemobiletools.dialer.extensions.config
import kotlinx.android.synthetic.main.dialog_change_sorting.view.*
class ChangeSortingDialog(val activity: BaseSimpleActivity, private val callback: () -> Unit) {
class ChangeSortingDialog(
val activity: BaseSimpleActivity,
private val showCustomSorting: Boolean = false,
private val callback: () -> Unit
) {
private var currSorting = 0
private var config = activity.config
private var view = activity.layoutInflater.inflate(R.layout.dialog_change_sorting, null)
@ -22,18 +30,38 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, private val callback
activity.setupDialogStuff(view, this, R.string.sort_by)
}
currSorting = config.sorting
currSorting = if (showCustomSorting && config.isCustomOrderSelected) {
SORT_BY_CUSTOM
} else {
config.sorting
}
setupSortRadio()
setupOrderRadio()
}
private fun setupSortRadio() {
val sortingRadio = view.sorting_dialog_radio_sorting
sortingRadio.setOnCheckedChangeListener { group, checkedId ->
val isCustomSorting = checkedId == sortingRadio.sorting_dialog_radio_custom.id
view.sorting_dialog_radio_order.beGoneIf(isCustomSorting)
view.divider.beGoneIf(isCustomSorting)
}
val sortBtn = when {
currSorting and SORT_BY_FULL_NAME != 0 -> sortingRadio.sorting_dialog_radio_full_name
currSorting and SORT_BY_CUSTOM != 0 -> sortingRadio.sorting_dialog_radio_custom
else -> sortingRadio.sorting_dialog_radio_date_created
}
sortBtn.isChecked = true
if (showCustomSorting) {
sortingRadio.sorting_dialog_radio_custom.isChecked = config.isCustomOrderSelected
}
view.sorting_dialog_radio_custom.beGoneIf(!showCustomSorting)
}
private fun setupOrderRadio() {
@ -50,14 +78,25 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, private val callback
val sortingRadio = view.sorting_dialog_radio_sorting
var sorting = when (sortingRadio.checkedRadioButtonId) {
R.id.sorting_dialog_radio_full_name -> SORT_BY_FULL_NAME
R.id.sorting_dialog_radio_custom -> SORT_BY_CUSTOM
else -> SORT_BY_DATE_CREATED
}
if (view.sorting_dialog_radio_order.checkedRadioButtonId == R.id.sorting_dialog_radio_descending) {
if (sorting != SORT_BY_CUSTOM && view.sorting_dialog_radio_order.checkedRadioButtonId == R.id.sorting_dialog_radio_descending) {
sorting = sorting or SORT_DESCENDING
}
config.sorting = sorting
if (showCustomSorting) {
if (sorting == SORT_BY_CUSTOM) {
config.isCustomOrderSelected = true
} else {
config.isCustomOrderSelected = false
config.sorting = sorting
}
} else {
config.sorting = sorting
}
SimpleContact.sorting = sorting
callback()
}

View File

@ -2,6 +2,7 @@ package com.simplemobiletools.dialer.fragments
import android.content.Context
import android.util.AttributeSet
import com.google.gson.Gson
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.CallConfirmationDialog
@ -16,6 +17,7 @@ import com.simplemobiletools.dialer.R
import com.simplemobiletools.dialer.activities.SimpleActivity
import com.simplemobiletools.dialer.adapters.ContactsAdapter
import com.simplemobiletools.dialer.extensions.config
import com.simplemobiletools.dialer.helpers.Converters
import com.simplemobiletools.dialer.interfaces.RefreshItemsListener
import kotlinx.android.synthetic.main.fragment_letters_layout.view.*
import java.util.*
@ -57,8 +59,12 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
allContacts.sort()
}
if (activity!!.config.isCustomOrderSelected) {
allContacts = sortByCustomOrder(allContacts)
}
activity?.runOnUiThread {
gotContacts(contacts)
gotContacts(allContacts)
callback?.invoke()
}
}
@ -75,7 +81,14 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
val currAdapter = fragment_list.adapter
if (currAdapter == null) {
ContactsAdapter(activity as SimpleActivity, contacts, fragment_list, this, showDeleteButton = false) {
ContactsAdapter(
activity = activity as SimpleActivity,
contacts = contacts,
recyclerView = fragment_list,
refreshItemsListener = this,
showDeleteButton = false,
enableDrag = true,
) {
if (context.config.showCallConfirmation) {
CallConfirmationDialog(activity as SimpleActivity, (it as SimpleContact).name) {
callContact(it)
@ -85,6 +98,15 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
}
}.apply {
fragment_list.adapter = this
onDragEndListener = {
val adapter = fragment_list?.adapter
if (adapter is ContactsAdapter) {
val items = adapter.contacts
saveCustomOrderToPrefs(items)
setupLetterFastscroller(items)
}
}
}
if (context.areSystemAnimationsEnabled) {
@ -96,6 +118,28 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
}
}
private fun sortByCustomOrder(favorites: List<SimpleContact>): ArrayList<SimpleContact> {
val favoritesOrder = activity!!.config.favoritesContactsOrder
if (favoritesOrder.isEmpty()) {
return ArrayList(favorites)
}
val orderList = Converters().jsonToStringList(favoritesOrder)
val map = orderList.withIndex().associate { it.value to it.index }
val sorted = favorites.sortedBy { map[it.contactId.toString()] }
return ArrayList(sorted)
}
private fun saveCustomOrderToPrefs(items: ArrayList<SimpleContact>) {
activity?.apply {
val orderIds = items.map { it.contactId }
val orderGsonString = Gson().toJson(orderIds)
config.favoritesContactsOrder = orderGsonString
}
}
private fun callContact(simpleContact: SimpleContact) {
val phoneNumbers = simpleContact.phoneNumbers
if (phoneNumbers.size <= 1) {

View File

@ -59,4 +59,12 @@ class Config(context: Context) : BaseConfig(context) {
var showTabs: Int
get() = prefs.getInt(SHOW_TABS, ALL_TABS_MASK)
set(showTabs) = prefs.edit().putInt(SHOW_TABS, showTabs).apply()
var favoritesContactsOrder: String
get() = prefs.getString(FAVORITES_CONTACTS_ORDER, "")!!
set(order) = prefs.edit().putString(FAVORITES_CONTACTS_ORDER, order).apply()
var isCustomOrderSelected: Boolean
get() = prefs.getBoolean(FAVORITES_CUSTOM_ORDER_SELECTED, false)
set(selected) = prefs.edit().putBoolean(FAVORITES_CUSTOM_ORDER_SELECTED, selected).apply()
}

View File

@ -12,6 +12,8 @@ const val OPEN_DIAL_PAD_AT_LAUNCH = "open_dial_pad_at_launch"
const val DISABLE_PROXIMITY_SENSOR = "disable_proximity_sensor"
const val DISABLE_SWIPE_TO_ANSWER = "disable_swipe_to_answer"
const val SHOW_TABS = "show_tabs"
const val FAVORITES_CONTACTS_ORDER = "favorites_contacts_order"
const val FAVORITES_CUSTOM_ORDER_SELECTED = "favorites_custom_order_selected"
const val ALL_TABS_MASK = TAB_CONTACTS or TAB_FAVORITES or TAB_CALL_HISTORY

View File

@ -0,0 +1,13 @@
package com.simplemobiletools.dialer.helpers
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class Converters {
private val gson = Gson()
private val stringType = object : TypeToken<List<String>>() {}.type
fun jsonToStringList(value: String) = gson.fromJson<ArrayList<String>>(value, stringType)
fun stringListToJson(list: ArrayList<String>) = gson.toJson(list)
}

View File

@ -35,9 +35,18 @@
android:paddingBottom="@dimen/medium_margin"
android:text="@string/date_created" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/sorting_dialog_radio_custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin"
android:text="@string/custom" />
</RadioGroup>
<include layout="@layout/divider" />
<include
android:id="@+id/divider"
layout="@layout/divider" />
<RadioGroup
android:id="@+id/sorting_dialog_radio_order"