Update Material Components library.

- Introduced the new material date range picker, to be used in the FilterOptionsDialog to select a date range. This was necessary because the old MaterialDatePicker that was used inside DatePickerFragment is no longer available in the latest version of the Material Components library.
This commit is contained in:
Severiano Jaramillo 2021-03-23 22:31:44 -07:00
parent dd1bf98e5d
commit 7e12224795
5 changed files with 46 additions and 163 deletions

View file

@ -78,7 +78,7 @@ dependencies {
// Google // Google
implementation 'com.google.zxing:core:3.4.0' implementation 'com.google.zxing:core:3.4.0'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.android.material:material:1.1.0-alpha04' implementation 'com.google.android.material:material:1.3.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0' implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.maps.android:android-maps-utils:0.5' implementation 'com.google.maps.android:android-maps-utils:0.5'
// AAC Lifecycle // AAC Lifecycle

View file

@ -11,8 +11,10 @@ import androidx.core.os.ConfigurationCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointBackward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
import cy.agorise.bitsybitshareswallet.databinding.DialogFilterOptionsBinding import cy.agorise.bitsybitshareswallet.databinding.DialogFilterOptionsBinding
@ -20,7 +22,6 @@ import cy.agorise.bitsybitshareswallet.models.FilterOptions
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.Helper import cy.agorise.bitsybitshareswallet.utils.Helper
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -30,15 +31,11 @@ import kotlin.collections.ArrayList
* Creates a Dialog that communicates with {@link TransactionsActivity} to give it parameters about * Creates a Dialog that communicates with {@link TransactionsActivity} to give it parameters about
* how to filter the list of Transactions * how to filter the list of Transactions
*/ */
class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListener { class FilterOptionsDialog : DialogFragment() {
companion object { // Container Fragment must implement this interface
private const val TAG = "FilterOptionsDialog" interface OnFilterOptionsSelectedListener {
fun onFilterOptionsSelected(filterOptions: FilterOptions)
const val KEY_FILTER_OPTIONS = "key_filter_options"
const val START_DATE_PICKER = 0
const val END_DATE_PICKER = 1
} }
private val viewModel: BalanceDetailViewModel by viewModels() private val viewModel: BalanceDetailViewModel by viewModels()
@ -61,44 +58,6 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
private lateinit var mCurrency: Currency private lateinit var mCurrency: Currency
override fun onDateSet(which: Int, timestamp: Long) {
when (which) {
START_DATE_PICKER -> {
mFilterOptions.startDate = timestamp
updateDateTextViews()
}
END_DATE_PICKER -> {
mFilterOptions.endDate = timestamp
// Make sure there is at least one moth difference between start and end time
val calendar = Calendar.getInstance()
calendar.timeInMillis = mFilterOptions.endDate
calendar.add(Calendar.MONTH, -1)
val tmpTime = calendar.timeInMillis
if (tmpTime < mFilterOptions.startDate)
mFilterOptions.startDate = tmpTime
updateDateTextViews()
}
}
}
private fun updateDateTextViews() {
var date = Date(mFilterOptions.startDate)
binding.tvStartDate.text = dateFormat.format(date)
date = Date(mFilterOptions.endDate)
binding.tvEndDate.text = dateFormat.format(date)
}
// Container Fragment must implement this interface
interface OnFilterOptionsSelectedListener {
fun onFilterOptionsSelected(filterOptions: FilterOptions)
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -136,9 +95,9 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
} }
binding.cbDateRange.isChecked = mFilterOptions.dateRangeAll binding.cbDateRange.isChecked = mFilterOptions.dateRangeAll
binding.tvStartDate.setOnClickListener(mDateClickListener) binding.tvStartDate.setOnClickListener { showDateRangePicker() }
binding.tvEndDate.setOnClickListener(mDateClickListener) binding.tvEndDate.setOnClickListener { showDateRangePicker() }
updateDateTextViews() updateDateTextViews()
@ -220,27 +179,38 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
} }
} }
private val mDateClickListener = View.OnClickListener { v -> private fun updateDateTextViews() {
val calendar = Calendar.getInstance() var date = Date(mFilterOptions.startDate)
binding.tvStartDate.text = dateFormat.format(date)
// Variable used to select that date on the calendar date = Date(mFilterOptions.endDate)
var currentTime = calendar.timeInMillis binding.tvEndDate.text = dateFormat.format(date)
var maxTime = currentTime
var which = -1
if (v.id == R.id.tvStartDate) {
which = START_DATE_PICKER
currentTime = mFilterOptions.startDate
calendar.timeInMillis = mFilterOptions.endDate
calendar.add(Calendar.MONTH, -1)
maxTime = calendar.timeInMillis
} else if (v.id == R.id.tvEndDate) {
which = END_DATE_PICKER
currentTime = mFilterOptions.endDate
} }
val datePickerFragment = DatePickerFragment.newInstance(which, currentTime, maxTime) private fun showDateRangePicker() {
datePickerFragment.show(childFragmentManager, "date-picker") // Makes only dates until today selectable.
val constraintsBuilder =
CalendarConstraints.Builder()
.setValidator(DateValidatorPointBackward.now())
val dateRangePicker =
MaterialDatePicker.Builder.dateRangePicker()
.setSelection(
androidx.core.util.Pair(
mFilterOptions.startDate,
mFilterOptions.endDate
)
)
.setCalendarConstraints(constraintsBuilder.build())
.build()
dateRangePicker.addOnPositiveButtonClickListener {
mFilterOptions.startDate = it.first!! // This is safe cause these should never be null
mFilterOptions.endDate = it.second!!
updateDateTextViews()
}
dateRangePicker.show(childFragmentManager, "date-picker")
} }
private fun validateFields() { private fun validateFields() {
@ -283,4 +253,10 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
mCallback!!.onFilterOptionsSelected(mFilterOptions) mCallback!!.onFilterOptionsSelected(mFilterOptions)
dismiss() dismiss()
} }
companion object {
private const val TAG = "FilterOptionsDialog"
const val KEY_FILTER_OPTIONS = "key_filter_options"
}
} }

View file

@ -1,93 +0,0 @@
package cy.agorise.bitsybitshareswallet.views
import android.app.DatePickerDialog
import android.app.Dialog
import android.os.Bundle
import android.widget.DatePicker
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import com.google.android.material.picker.MaterialDatePickerDialog
import java.util.*
/**
* Lets the user select a Date and communicates the selection back to the parent fragment
* using the OnDateSetListener interface, which has to be implemented by the parent.
*/
class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {
companion object {
const val TAG = "DatePickerFragment"
const val KEY_WHICH = "key_which"
const val KEY_CURRENT = "key_current"
const val KEY_MAX = "key_max"
fun newInstance(which: Int, currentTime: Long, maxTime: Long): DatePickerFragment {
val f = DatePickerFragment()
val bundle = Bundle()
bundle.putInt(KEY_WHICH, which)
bundle.putLong(KEY_CURRENT, currentTime)
bundle.putLong(KEY_MAX, maxTime)
f.arguments = bundle
return f
}
}
/**
* Callback used to communicate the date selection back to the parent
*/
private var mCallback: OnDateSetListener? = null
private var which: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
which = arguments!!.getInt(KEY_WHICH)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
onAttachToParentFragment(parentFragment)
val currentTime = arguments!!.getLong(KEY_CURRENT)
val maxTime = arguments!!.getLong(KEY_MAX)
// Use the current date as the default date in the picker
val calendar = Calendar.getInstance()
calendar.timeInMillis = currentTime
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
// Create a new instance of DatePickerDialog and return it
val datePicker = MaterialDatePickerDialog(activity!!, this, year, month, day)
// Set maximum date allowed to today
datePicker.datePicker.maxDate = maxTime
return datePicker
}
override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
val calendar = GregorianCalendar()
calendar.set(year, month, day)
mCallback?.onDateSet(which, calendar.time.time)
}
/**
* Attaches the current [DialogFragment] to its [Fragment] parent, to initialize the
* [OnDateSetListener] interface
*/
private fun onAttachToParentFragment(fragment: Fragment?) {
try {
mCallback = fragment as OnDateSetListener
} catch (e: ClassCastException) {
throw ClassCastException("$fragment must implement OnDateSetListener")
}
}
// Container Activity must implement this interface
interface OnDateSetListener {
fun onDateSet(which: Int, timestamp: Long)
}
}

View file

@ -13,7 +13,7 @@ import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
* A TextInputEditText that hides the keyboard when the focus is removed from it and also lets you * A TextInputEditText that hides the keyboard when the focus is removed from it and also lets you
* use actions ("Done", "Go", etc.) on multi-line edits. * use actions ("Done", "Go", etc.) on multi-line edits.
*/ */
class MyTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){ class MyTextInputEditText(context: Context, attrs: AttributeSet?) : TextInputEditText(context, attrs){
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? { override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
val connection = super.onCreateInputConnection(outAttrs) val connection = super.onCreateInputConnection(outAttrs)

View file

@ -138,7 +138,7 @@
<!-- Ignore Agorise Fees --> <!-- Ignore Agorise Fees -->
<Switch <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchAgoriseFees" android:id="@+id/switchAgoriseFees"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"