Create FilterOptionsDialog, which opens when the user clicks the filter icon in the Transactions toolbar. This dialog communicates back a forth with the TransactionsFragment to update the selected filter options via the OnFilterOptionsSelectedListener interface.

This commit is contained in:
Severiano Jaramillo 2018-12-26 11:59:02 -06:00
parent e4391dad73
commit ddf7f16c2b
4 changed files with 555 additions and 5 deletions

View file

@ -0,0 +1,316 @@
package cy.agorise.bitsybitshareswallet.fragments
import android.app.Dialog
import android.content.res.Resources
import android.os.Bundle
import android.os.Handler
import android.os.Message
import androidx.fragment.app.DialogFragment
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import cy.agorise.bitsybitshareswallet.R
import java.text.SimpleDateFormat
import java.util.*
import kotlin.ClassCastException
/**
* Creates a Dialog that communicates with {@link TransactionsActivity} to give it parameters about
* how to filter the list of Transactions
*/
class FilterOptionsDialog : DialogFragment() {
// Widgets TODO use android-kotlin-extensions {onViewCreated}
lateinit var rbTransactionAll: RadioButton
lateinit var rbTransactionSent: RadioButton
lateinit var rbTransactionReceived: RadioButton
lateinit var cbDateRange: CheckBox
lateinit var llDateRange: LinearLayout
lateinit var tvStartDate: TextView
lateinit var tvEndDate: TextView
lateinit var cbCryptocurrency: CheckBox
lateinit var sCryptocurrency: Spinner
lateinit var cbFiatAmount: CheckBox
lateinit var llFiatAmount: LinearLayout
// lateinit var etFromFiatAmount: CurrencyEditText
// lateinit var etToFiatAmount: CurrencyEditText
private var mCallback: OnFilterOptionsSelectedListener? = null
private var mDatePickerHandler: DatePickerHandler? = null
private var dateFormat: SimpleDateFormat = SimpleDateFormat("d/MMM/yyyy",
Resources.getSystem().configuration.locale)
private var startDate: Long = 0
private var endDate: Long = 0
// /**
// * Variable used to keep track of the current user's currency
// */
// private val mUserCurrency = RuntimeData.EXTERNAL_CURRENCY
companion object {
const val KEY_FILTER_TRANSACTION_DIRECTION = "key_filter_transaction_direction"
const val KEY_FILTER_DATE_RANGE_ALL = "key_filter_date_range_all"
const val KEY_FILTER_START_DATE = "key_filter_start_date"
const val KEY_FILTER_END_DATE = "key_filter_end_date"
const val KEY_FILTER_CRYPTOCURRENCY_ALL = "key_filter_cryptocurrency_all"
const val KEY_FILTER_CRYPTOCURRENCY = "key_filter_cryptocurrency"
const val KEY_FILTER_FIAT_AMOUNT_ALL = "key_filter_fiat_amount_all"
const val KEY_FILTER_FROM_FIAT_AMOUNT = "filter_from_fiat_amount"
const val KEY_FILTER_TO_FIAT_AMOUNT = "filter_to_fiat_amount"
const val KEY_TIMESTAMP = "key_timestamp"
const val START_DATE_PICKER = 0
const val END_DATE_PICKER = 1
fun newInstance(filterTransactionsDirection: Int, filterDateRangeAll: Boolean,
filterStartDate: Long, filterEndDate: Long, filterCryptocurrencyAll: Boolean,
filterCryptocurrency: String, filterFiatAmountAll: Boolean,
filterFromFiatAmount: Long, filterToFiatAmount: Long): FilterOptionsDialog {
val frag = FilterOptionsDialog()
val args = Bundle()
args.putInt(KEY_FILTER_TRANSACTION_DIRECTION, filterTransactionsDirection)
args.putBoolean(KEY_FILTER_DATE_RANGE_ALL, filterDateRangeAll)
args.putLong(KEY_FILTER_START_DATE, filterStartDate)
args.putLong(KEY_FILTER_END_DATE, filterEndDate)
args.putBoolean(KEY_FILTER_CRYPTOCURRENCY_ALL, filterCryptocurrencyAll)
args.putString(KEY_FILTER_CRYPTOCURRENCY, filterCryptocurrency)
args.putBoolean(KEY_FILTER_FIAT_AMOUNT_ALL, filterFiatAmountAll)
args.putLong(KEY_FILTER_FROM_FIAT_AMOUNT, filterFromFiatAmount)
args.putLong(KEY_FILTER_TO_FIAT_AMOUNT, filterToFiatAmount)
frag.arguments = args
return frag
}
}
/**
* DatePicker message handler.
*/
inner class DatePickerHandler : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
val bundle = msg.data
val timestamp = bundle.get(KEY_TIMESTAMP) as Long
//Log.d(TAG, "timestamp: $timestamp")
when (msg.arg1) {
START_DATE_PICKER -> {
startDate = timestamp
updateDateTextViews()
}
END_DATE_PICKER -> {
endDate = timestamp
// Make sure there is at least one moth difference between start and end time
val calendar = Calendar.getInstance()
calendar.timeInMillis = endDate
calendar.add(Calendar.MONTH, -1)
val tmpTime = calendar.timeInMillis
if (tmpTime < startDate)
startDate = tmpTime
updateDateTextViews()
}
}
}
}
private fun updateDateTextViews() {
var date = Date(startDate)
tvStartDate.text = dateFormat.format(date)
date = Date(endDate)
tvEndDate.text = dateFormat.format(date)
}
// Container Activity must implement this interface
interface OnFilterOptionsSelectedListener {
fun onFilterOptionsSelected(filterTransactionsDirection: Int,
filterDateRangeAll: Boolean,
filterStartDate: Long,
filterEndDate: Long,
filterCryptocurrencyAll: Boolean,
filterCryptocurrency: String,
filterFiatAmountAll: Boolean,
filterFromFiatAmount: Long,
filterToFiatAmount: Long)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
onAttachToParentFragment(parentFragment!!)
// Initialize handler for communication with the DatePicker
mDatePickerHandler = DatePickerHandler()
val builder = AlertDialog.Builder(context!!)
.setTitle("Filter options")
.setPositiveButton("Filter") { _, _ -> validateFields() }
.setNegativeButton("Cancel") { _, _ -> dismiss() }
// Inflate layout
val inflater = activity!!.layoutInflater
val view = inflater.inflate(R.layout.dialog_filter_options, null)
// Initialize Transactions direction
rbTransactionAll = view.findViewById(R.id.rbTransactionAll)
rbTransactionSent = view.findViewById(R.id.rbTransactionSent)
rbTransactionReceived = view.findViewById(R.id.rbTransactionReceived)
val radioButtonChecked = arguments!!.getInt(KEY_FILTER_TRANSACTION_DIRECTION, 0)
when (radioButtonChecked) {
0 -> rbTransactionAll.isChecked = true
1 -> rbTransactionSent.isChecked = true
2 -> rbTransactionReceived.isChecked = true
}
// Initialize Date range
cbDateRange = view.findViewById(R.id.cbDateRange)
// llDateRange = view.findViewById(R.id.llDateRange)
// cbDateRange.setOnCheckedChangeListener { _, isChecked ->
// llDateRange.visibility = if(isChecked) View.GONE else View.VISIBLE }
cbDateRange.isChecked = arguments!!.getBoolean(KEY_FILTER_DATE_RANGE_ALL, true)
//
// tvStartDate = view.findViewById(R.id.tvStartDate)
// tvEndDate = view.findViewById(R.id.tvEndDate)
//
// startDate = arguments!!.getLong(KEY_FILTER_START_DATE, 0)
// tvStartDate.setOnClickListener(mDateClickListener)
//
// endDate = arguments!!.getLong(KEY_FILTER_END_DATE, 0)
// tvEndDate.setOnClickListener(mDateClickListener)
//
// updateDateTextViews()
// Initialize Cryptocurrency
cbCryptocurrency = view.findViewById(R.id.cbCryptocurrency)
// sCryptocurrency = view.findViewById(R.id.sCryptocurrency)
// cbCryptocurrency.setOnCheckedChangeListener { _, isChecked ->
// sCryptocurrency.visibility = if(isChecked) View.GONE else View.VISIBLE }
cbCryptocurrency.isChecked = arguments!!.getBoolean(KEY_FILTER_CRYPTOCURRENCY_ALL, true)
// sCryptocurrency = view.findViewById(R.id.sCryptocurrency)
// initializeCryptocurrencySpinner()
// Initialize Fiat amount
cbFiatAmount = view.findViewById(R.id.cbFiatAmount)
// llFiatAmount = view.findViewById(R.id.llFiatAmount)
// cbFiatAmount.setOnCheckedChangeListener { _, isChecked ->
// llFiatAmount.visibility = if(isChecked) View.GONE else View.VISIBLE }
cbFiatAmount.isChecked = arguments!!.getBoolean(KEY_FILTER_FIAT_AMOUNT_ALL, true)
// val locale = Resources.getSystem().configuration.locale
//
// etFromFiatAmount = view.findViewById(R.id.etFromFiatAmount)
// etFromFiatAmount.locale = locale
// val fromFiatAmount = arguments!!.getLong(KEY_FILTER_FROM_FIAT_AMOUNT, 0)
// etFromFiatAmount.setText("$fromFiatAmount", TextView.BufferType.EDITABLE)
//
// etToFiatAmount = view.findViewById(R.id.etToFiatAmount)
// etToFiatAmount.locale = locale
// val toFiatAmount = arguments!!.getLong(KEY_FILTER_TO_FIAT_AMOUNT, 0)
// etToFiatAmount.setText("$toFiatAmount", TextView.BufferType.EDITABLE)
builder.setView(view)
return builder.create()
}
/**
* Attaches the current [DialogFragment] to its [Fragment] parent, to initialize the
* [OnFilterOptionsSelectedListener] interface
*/
private fun onAttachToParentFragment(fragment: Fragment) {
try {
mCallback = fragment as OnFilterOptionsSelectedListener
} catch (e: ClassCastException) {
throw ClassCastException(fragment.toString() + " must implement OnFilterOptionsSelectedListener")
}
}
// private fun initializeCryptocurrencySpinner() {
// val cryptoCurrencyList = database!!.getSortedCryptoCurrencies(false,
// SortType.DESCENDING, true)
//
// val cryptocurrencySpinnerAdapter = CryptocurrencySpinnerAdapter(context!!,
// R.layout.item_cryptocurrency,
// R.id.tvCryptocurrencyName,
// cryptoCurrencyList)
//
// sCryptocurrency.adapter = cryptocurrencySpinnerAdapter
//
// val cryptocurrencySelected = arguments!!.getString(KEY_FILTER_CRYPTOCURRENCY)
//
// val index = Math.max(cryptocurrencySpinnerAdapter.getPosition(database!!.getCryptocurrencyBySymbol(
// cryptocurrencySelected)), 0)
//
// sCryptocurrency.setSelection(index)
// }
// private val mDateClickListener = View.OnClickListener { v ->
// val calendar = Calendar.getInstance()
//
// // Variable used to select that date on the calendar
// var currentTime = calendar.timeInMillis
// var maxTime = currentTime
//
// var which = -1
// if (v.id == R.id.tvStartDate) {
// which = START_DATE_PICKER
// currentTime = startDate
// calendar.timeInMillis = endDate
// calendar.add(Calendar.MONTH, -1)
// maxTime = calendar.timeInMillis
// } else if (v.id == R.id.tvEndDate) {
// which = END_DATE_PICKER
// currentTime = endDate
// }
//
// val datePickerFragment = DatePickerFragment.newInstance(which, currentTime,
// maxTime, mDatePickerHandler)
// datePickerFragment.show(activity!!.supportFragmentManager, "date-picker")
// }
private fun validateFields() {
val filterTransactionsDirection = 0 //when {
// rbTransactionAll.isChecked -> 0
// rbTransactionSent.isChecked -> 1
// rbTransactionReceived.isChecked -> 2
// else -> { 0 }
// }
val filterDateRangeAll = cbDateRange.isChecked
val filterCryptocurrencyAll = cbCryptocurrency.isChecked
val filterCryptocurrency = "" //(sCryptocurrency.selectedItem as CryptoCurrency).symbol
val filterFiatAmountAll = cbFiatAmount.isChecked
val filterFromFiatAmount = 0L//(etFromFiatAmount.currencyDouble *
// Math.pow(10.0, mUserCurrency.defaultFractionDigits.toDouble())).toLong()
var filterToFiatAmount = 1L//(etToFiatAmount.currencyDouble *
// Math.pow(10.0, mUserCurrency.defaultFractionDigits.toDouble())).toLong()
// Make sure ToFiatAmount is at least 50 units bigger than FromFiatAmount
// if (!filterFiatAmountAll && filterToFiatAmount <= filterFromFiatAmount) {
// filterToFiatAmount = filterFromFiatAmount + 50 *
// Math.pow(10.0, mUserCurrency.defaultFractionDigits.toDouble()).toLong()
// }
mCallback!!.onFilterOptionsSelected(filterTransactionsDirection, filterDateRangeAll,
startDate, endDate, filterCryptocurrencyAll, filterCryptocurrency, filterFiatAmountAll,
filterFromFiatAmount, filterToFiatAmount)
}
}

View file

@ -24,7 +24,7 @@ import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class TransactionsFragment : Fragment() { class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSelectedListener {
private lateinit var mTransferDetailViewModel: TransferDetailViewModel private lateinit var mTransferDetailViewModel: TransferDetailViewModel
@ -87,7 +87,7 @@ class TransactionsFragment : Fragment() {
inflater.inflate(R.menu.menu_transactions, menu) inflater.inflate(R.menu.menu_transactions, menu)
// Adds listener for the SearchView // Adds listener for the SearchView
val searchItem = menu.findItem(R.id.menuSearch) val searchItem = menu.findItem(R.id.menu_search)
val searchView = searchItem.actionView as SearchView val searchView = searchItem.actionView as SearchView
mDisposables.add( mDisposables.add(
searchView.queryTextChangeEvents() searchView.queryTextChangeEvents()
@ -105,6 +105,25 @@ class TransactionsFragment : Fragment() {
searchView.maxWidth = getScreenWidth(activity) * 3 / 5 searchView.maxWidth = getScreenWidth(activity) * 3 / 5
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.menu_filter -> {
val filterOptionsDialog = FilterOptionsDialog.newInstance(
filterTransactionsDirection, filterDateRangeAll, filterStartDate * 1000,
filterEndDate * 1000, filterCryptocurrencyAll, filterCryptocurrency,
filterFiatAmountAll, filterFromFiatAmount, filterToFiatAmount
)
filterOptionsDialog.show(childFragmentManager, "filter-options-tag")
true
}
R.id.menu_export -> {
// TODO add export options
true
}
else -> super.onOptionsItemSelected(item)
}
}
/** /**
* Returns the screen width in pixels for the given [FragmentActivity] * Returns the screen width in pixels for the given [FragmentActivity]
*/ */
@ -163,6 +182,32 @@ class TransactionsFragment : Fragment() {
rvTransactions.scrollToPosition(0) rvTransactions.scrollToPosition(0)
} }
/**
*
*/
override fun onFilterOptionsSelected(
filterTransactionsDirection: Int,
filterDateRangeAll: Boolean,
filterStartDate: Long,
filterEndDate: Long,
filterCryptocurrencyAll: Boolean,
filterCryptocurrency: String,
filterFiatAmountAll: Boolean,
filterFromFiatAmount: Long,
filterToFiatAmount: Long
) {
this.filterTransactionsDirection = filterTransactionsDirection
this.filterDateRangeAll = filterDateRangeAll
this.filterStartDate = filterStartDate / 1000
this.filterEndDate = filterEndDate / 1000
this.filterCryptocurrencyAll = filterCryptocurrencyAll
this.filterCryptocurrency = filterCryptocurrency
this.filterFiatAmountAll = filterFiatAmountAll
this.filterFromFiatAmount = filterFromFiatAmount
this.filterToFiatAmount = filterToFiatAmount
applyFilterOptions(true)
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:paddingTop="@dimen/spacing_different_topic"
android:paddingStart="@dimen/spacing_different_topic"
android:paddingEnd="@dimen/spacing_different_topic"
android:paddingBottom="@dimen/spacing_same_topic">
<TextView
android:id="@+id/tvTransactionDirection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Transactions"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="parent"/>
<RadioGroup
android:id="@+id/rgTransactionDirection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/tvTransactionDirection">
<RadioButton
android:id="@+id/rbTransactionAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="All"/>
<RadioButton
android:id="@+id/rbTransactionSent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sent"/>
<RadioButton
android:id="@+id/rbTransactionReceived"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Received"/>
</RadioGroup>
<!-- Date Range -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Date Range"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="@id/cbDateRange"
app:layout_constraintStart_toStartOf="parent"/>
<CheckBox
android:id="@+id/cbDateRange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_different_topic"
android:text="All"
app:layout_constraintTop_toBottomOf="@id/rgTransactionDirection"
app:layout_constraintEnd_toEndOf="parent"/>
<LinearLayout
android:id="@+id/llDateRange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/cbDateRange"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tvStartDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="12/08/2017"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="@string/text__to"/>
<TextView
android:id="@+id/tvEndDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="12/09/2017"/>
</LinearLayout>
<!-- Cryptocurrency -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Cryptocurrency"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="@id/cbCryptocurrency"
app:layout_constraintStart_toStartOf="parent"/>
<CheckBox
android:id="@+id/cbCryptocurrency"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_different_topic"
android:text="All"
app:layout_constraintTop_toBottomOf="@+id/llDateRange"
app:layout_constraintEnd_toEndOf="parent"/>
<Spinner
android:id="@+id/sCryptocurrency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@android:layout/simple_list_item_1"
app:layout_constraintTop_toBottomOf="@id/cbCryptocurrency"/>
<!-- Fiat Amount -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="Fiat Amount"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="@id/cbFiatAmount"
app:layout_constraintStart_toStartOf="parent"/>
<CheckBox
android:id="@+id/cbFiatAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_same_topic"
android:text="All"
android:enabled="false"
app:layout_constraintTop_toBottomOf="@id/sCryptocurrency"
app:layout_constraintEnd_toEndOf="parent"/>
<!--<LinearLayout-->
<!--android:id="@+id/llFiatAmount"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_margin="4dp"-->
<!--android:orientation="horizontal"-->
<!--app:layout_constraintTop_toBottomOf="@id/cbFiatAmount">-->
<!--<TextView-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:text="@string/between"/>-->
<!--<faranjit.currency.edittext.CurrencyEditText-->
<!--android:id="@+id/etFromFiatAmount"-->
<!--android:layout_width="0dp"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_weight="1"-->
<!--android:inputType="number"-->
<!--android:textAlignment="center"-->
<!--app:showSymbol="true"/>-->
<!--<TextView-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:text="@string/and"/>-->
<!--<faranjit.currency.edittext.CurrencyEditText-->
<!--android:id="@+id/etToFiatAmount"-->
<!--android:layout_width="0dp"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_weight="1"-->
<!--android:inputType="number"-->
<!--android:textAlignment="center"-->
<!--app:showSymbol="true"/>-->
<!--</LinearLayout>-->
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -3,21 +3,21 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menuSearch" <item android:id="@+id/menu_search"
android:icon="@drawable/ic_search" android:icon="@drawable/ic_search"
android:title="@string/title_search" android:title="@string/title_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always"/> app:showAsAction="always"/>
<item <item
android:id="@+id/menuFilter" android:id="@+id/menu_filter"
android:icon="@drawable/ic_filter" android:icon="@drawable/ic_filter"
app:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/title_filter"> android:title="@string/title_filter">
</item> </item>
<item <item
android:id="@+id/menuExport" android:id="@+id/menu_export"
android:icon="@drawable/ic_file_download" android:icon="@drawable/ic_file_download"
app:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/title_export"> android:title="@string/title_export">