fixes to scoping and job cancellation
Utilize fragmentscope in the AndroidInjector subcomponents for fragments and also allow presenters to restart jobs
This commit is contained in:
parent
ab58b03db6
commit
e79c68fd20
|
@ -3,4 +3,5 @@ package cash.z.android.wallet.di.annotation
|
|||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
annotation class ActivityScoped
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ActivityScope
|
|
@ -0,0 +1,7 @@
|
|||
package cash.z.android.wallet.di.annotation
|
||||
|
||||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ApplicationScope
|
|
@ -3,4 +3,5 @@ package cash.z.android.wallet.di.annotation
|
|||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
annotation class ApplicationScoped
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class FragmentScope
|
|
@ -23,9 +23,9 @@ import javax.inject.Singleton
|
|||
MainActivityModule::class,
|
||||
|
||||
// Injected Fragments
|
||||
HomeFragmentModule::class,
|
||||
AboutFragmentModule::class,
|
||||
HistoryFragmentModule::class,
|
||||
HomeFragmentModule::class,
|
||||
WelcomeFragmentModule::class,
|
||||
ReceiveFragmentModule::class,
|
||||
RequestFragmentModule::class,
|
||||
|
|
|
@ -56,10 +56,10 @@ class MainActivity : BaseActivity() {
|
|||
// used to manage the drawer and drawerToggle interactions
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
lateinit var navController: NavController
|
||||
private val multiStartNavigationUi = MultiStartNavigationUI(listOf(
|
||||
R.id.nav_home_fragment,
|
||||
R.id.nav_welcome_fragment
|
||||
))
|
||||
// private val multiStartNavigationUi = MultiStartNavigationUI(listOf(
|
||||
// R.id.nav_home_fragment,
|
||||
// R.id.nav_welcome_fragment
|
||||
// ))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -114,10 +114,10 @@ class MainActivity : BaseActivity() {
|
|||
navController = Navigation.findNavController(this, R.id.nav_host_fragment).also { n ->
|
||||
appBarConfiguration = AppBarConfiguration(n.graph, binding.drawerLayout).also { a ->
|
||||
binding.navView.setupWithNavController(n)
|
||||
multiStartNavigationUi.setupActionBarWithNavController(this, n, binding.drawerLayout)
|
||||
setupActionBarWithNavController(n, binding.drawerLayout)
|
||||
}
|
||||
}
|
||||
navController.addOnNavigatedListener { _, _ ->
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
// hide the keyboard anytime we change destinations
|
||||
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(binding.navView.windowToken, HIDE_NOT_ALWAYS)
|
||||
}
|
||||
|
@ -170,80 +170,80 @@ abstract class MainActivityModule {
|
|||
abstract fun contributeMainActivity(): MainActivity
|
||||
}
|
||||
|
||||
|
||||
class MultiStartNavigationUI(private val startDestinations: List<Int>) {
|
||||
fun setupActionBarWithNavController(activity: AppCompatActivity, navController: NavController,
|
||||
drawerLayout: DrawerLayout?) {
|
||||
|
||||
navController.addOnNavigatedListener(ActionBarOnNavigatedListener(
|
||||
activity, startDestinations, drawerLayout))
|
||||
}
|
||||
|
||||
fun navigateUp(drawerLayout: DrawerLayout?, navController: NavController): Boolean {
|
||||
if (drawerLayout != null && startDestinations.contains(navController.currentDestination?.id)) {
|
||||
drawerLayout.openDrawer(GravityCompat.START)
|
||||
return true
|
||||
} else {
|
||||
return navController.navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackPressed(activity: AppCompatActivity,
|
||||
navController: NavController): Boolean {
|
||||
if (startDestinations.contains(navController.currentDestination?.id)) {
|
||||
ActivityCompat.finishAfterTransition(activity)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private class ActionBarOnNavigatedListener(
|
||||
private val mActivity: AppCompatActivity,
|
||||
private val startDestinations: List<Int>,
|
||||
private val mDrawerLayout: DrawerLayout?
|
||||
) : NavController.OnNavigatedListener {
|
||||
private var mArrowDrawable: DrawerArrowDrawable? = null
|
||||
private var mAnimator: ValueAnimator? = null
|
||||
|
||||
override fun onNavigated(controller: NavController, destination: NavDestination) {
|
||||
val actionBar = mActivity.supportActionBar
|
||||
|
||||
val title = destination.label
|
||||
if (!title.isNullOrEmpty()) {
|
||||
actionBar?.title = title
|
||||
}
|
||||
|
||||
val isStartDestination = startDestinations.contains(destination.id)
|
||||
actionBar?.setDisplayHomeAsUpEnabled(this.mDrawerLayout != null || !isStartDestination)
|
||||
setActionBarUpIndicator(mDrawerLayout != null && isStartDestination)
|
||||
}
|
||||
|
||||
|
||||
private fun setActionBarUpIndicator(showAsDrawerIndicator: Boolean) {
|
||||
val delegate = mActivity.drawerToggleDelegate
|
||||
var animate = true
|
||||
if (mArrowDrawable == null) {
|
||||
mArrowDrawable = DrawerArrowDrawable(delegate!!.actionBarThemedContext)
|
||||
delegate.setActionBarUpIndicator(mArrowDrawable, 0)
|
||||
animate = false
|
||||
}
|
||||
|
||||
mArrowDrawable?.let {
|
||||
val endValue = if (showAsDrawerIndicator) 0.0f else 1.0f
|
||||
|
||||
if (animate) {
|
||||
val startValue = it.progress
|
||||
mAnimator?.cancel()
|
||||
|
||||
@SuppressLint("ObjectAnimatorBinding")
|
||||
mAnimator = ObjectAnimator.ofFloat(it, "progress", startValue, endValue)
|
||||
mAnimator?.start()
|
||||
} else {
|
||||
it.progress = endValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//class MultiStartNavigationUI(private val startDestinations: List<Int>) {
|
||||
// fun setupActionBarWithNavController(activity: AppCompatActivity, navController: NavController,
|
||||
// drawerLayout: DrawerLayout?) {
|
||||
//
|
||||
// navController.addOnNavigatedListener(ActionBarOnNavigatedListener(
|
||||
// activity, startDestinations, drawerLayout))
|
||||
// }
|
||||
//
|
||||
// fun navigateUp(drawerLayout: DrawerLayout?, navController: NavController): Boolean {
|
||||
// if (drawerLayout != null && startDestinations.contains(navController.currentDestination?.id)) {
|
||||
// drawerLayout.openDrawer(GravityCompat.START)
|
||||
// return true
|
||||
// } else {
|
||||
// return navController.navigateUp()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun onBackPressed(activity: AppCompatActivity,
|
||||
// navController: NavController): Boolean {
|
||||
// if (startDestinations.contains(navController.currentDestination?.id)) {
|
||||
// ActivityCompat.finishAfterTransition(activity)
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// private class ActionBarOnNavigatedListener(
|
||||
// private val mActivity: AppCompatActivity,
|
||||
// private val startDestinations: List<Int>,
|
||||
// private val mDrawerLayout: DrawerLayout?
|
||||
// ) : NavController.OnNavigatedListener {
|
||||
// private var mArrowDrawable: DrawerArrowDrawable? = null
|
||||
// private var mAnimator: ValueAnimator? = null
|
||||
//
|
||||
// override fun onNavigated(controller: NavController, destination: NavDestination) {
|
||||
// val actionBar = mActivity.supportActionBar
|
||||
//
|
||||
// val title = destination.label
|
||||
// if (!title.isNullOrEmpty()) {
|
||||
// actionBar?.title = title
|
||||
// }
|
||||
//
|
||||
// val isStartDestination = startDestinations.contains(destination.id)
|
||||
// actionBar?.setDisplayHomeAsUpEnabled(this.mDrawerLayout != null || !isStartDestination)
|
||||
// setActionBarUpIndicator(mDrawerLayout != null && isStartDestination)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private fun setActionBarUpIndicator(showAsDrawerIndicator: Boolean) {
|
||||
// val delegate = mActivity.drawerToggleDelegate
|
||||
// var animate = true
|
||||
// if (mArrowDrawable == null) {
|
||||
// mArrowDrawable = DrawerArrowDrawable(delegate!!.actionBarThemedContext)
|
||||
// delegate.setActionBarUpIndicator(mArrowDrawable, 0)
|
||||
// animate = false
|
||||
// }
|
||||
//
|
||||
// mArrowDrawable?.let {
|
||||
// val endValue = if (showAsDrawerIndicator) 0.0f else 1.0f
|
||||
//
|
||||
// if (animate) {
|
||||
// val startValue = it.progress
|
||||
// mAnimator?.cancel()
|
||||
//
|
||||
// @SuppressLint("ObjectAnimatorBinding")
|
||||
// mAnimator = ObjectAnimator.ofFloat(it, "progress", startValue, endValue)
|
||||
// mAnimator?.start()
|
||||
// } else {
|
||||
// it.progress = endValue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -6,6 +6,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.R
|
||||
|
@ -38,7 +39,12 @@ class FirstrunFragment : ProgressFragment(R.id.progress_firstrun), Transition.Tr
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
binding.buttonNext.setOnClickListener {
|
||||
mainActivity?.navController?.navigate(R.id.nav_sync_fragment)
|
||||
mainActivity?.navController?.navigate(
|
||||
R.id.action_firstrun_fragment_to_sync_fragment,
|
||||
null,
|
||||
NavOptions.Builder().setPopUpTo(R.id.mobile_navigation, true).build(),
|
||||
null
|
||||
)
|
||||
}
|
||||
// binding.buttonNext.alpha = 0f
|
||||
// binding.textProgressFirstrun.alpha = 0f
|
||||
|
|
|
@ -11,15 +11,13 @@ import cash.z.android.wallet.R
|
|||
import cash.z.android.wallet.databinding.FragmentHistoryBinding
|
||||
import cash.z.android.wallet.ui.adapter.TransactionAdapter
|
||||
import cash.z.android.wallet.ui.presenter.HistoryPresenter
|
||||
import cash.z.android.wallet.ui.presenter.Presenter
|
||||
import cash.z.android.wallet.ui.presenter.HistoryPresenterModule
|
||||
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
|
||||
|
@ -73,10 +71,6 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
|
|||
|
||||
@Module
|
||||
abstract class HistoryFragmentModule {
|
||||
@ContributesAndroidInjector
|
||||
@ContributesAndroidInjector(modules = [HistoryPresenterModule::class])
|
||||
abstract fun contributeHistoryFragment(): HistoryFragment
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun providePresenter(historyPresenter: HistoryPresenter): Presenter
|
||||
}
|
|
@ -22,10 +22,12 @@ import androidx.transition.Transition
|
|||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentHomeBinding
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.extention.*
|
||||
import cash.z.android.wallet.sample.SampleProperties
|
||||
import cash.z.android.wallet.ui.adapter.TransactionAdapter
|
||||
import cash.z.android.wallet.ui.presenter.HomePresenter
|
||||
import cash.z.android.wallet.ui.presenter.HomePresenterModule
|
||||
import cash.z.android.wallet.ui.presenter.Presenter
|
||||
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
|
||||
import cash.z.android.wallet.ui.util.LottieLooper
|
||||
|
@ -43,7 +45,6 @@ import dagger.Module
|
|||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextLong
|
||||
|
||||
|
@ -59,6 +60,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
|
||||
private lateinit var binding: FragmentHomeBinding
|
||||
private lateinit var zcashLogoAnimation: LottieLooper
|
||||
private var maxTransactionsShown: Int = 12
|
||||
private var snackbar: Snackbar? = null
|
||||
private var viewsInitialized = false
|
||||
|
||||
|
@ -66,6 +68,11 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
private var clock: Handler = Handler()
|
||||
private val tickIfNeeded = Ticker()
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
|
@ -89,7 +96,6 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initTemp()
|
||||
|
@ -128,15 +134,64 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// SendView Implementation
|
||||
//
|
||||
|
||||
override fun setTransactions(transactions: List<WalletTransaction>) {
|
||||
val recent = if(transactions.size > maxTransactionsShown) transactions.subList(0, maxTransactionsShown) else transactions
|
||||
with (binding.includeContent.recyclerTransactions) {
|
||||
(adapter as TransactionAdapter).submitList(recent)
|
||||
postDelayed({
|
||||
smoothScrollToPosition(0)
|
||||
}, 100L)
|
||||
}
|
||||
// Show "See All" when we have a sublist on screen
|
||||
if (recent.size != transactions.size) {
|
||||
binding.includeContent.textTransactionHeaderSeeAll.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
onContentRefreshComplete(transactions.isEmpty())
|
||||
}
|
||||
|
||||
//TODO: pull some of this logic into the presenter, particularly the part that deals with ZEC <-> USD price conversion
|
||||
override fun updateBalance(old: Long, new: Long) {
|
||||
val zecValue = new.convertZatoshiToZec()
|
||||
setZecValue(zecValue.toZecString(3))
|
||||
setUsdValue(zecValue.convertZecToUsd(SampleProperties.USD_PER_ZEC).toUsdString())
|
||||
|
||||
onContentRefreshComplete(new <= 0)
|
||||
}
|
||||
|
||||
override fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
|
||||
if (activeTransactionMap.isEmpty()) {
|
||||
twig("A.T.: setActiveTransactionsShown(false) because map is empty")
|
||||
setActiveTransactionsShown(false)
|
||||
return
|
||||
}
|
||||
|
||||
val transactions = activeTransactionMap.entries.toTypedArray()
|
||||
// primary is the last one that was inserted
|
||||
val primaryEntry = transactions[transactions.size - 1]
|
||||
updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
|
||||
|
||||
onContentRefreshComplete(false)
|
||||
}
|
||||
|
||||
override fun onCancelledTooLate() {
|
||||
snackbar = snackbar.showOk(view!!, "Oops! It was too late to cancel!")
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// View API
|
||||
//
|
||||
|
||||
fun setContentViewShown(isShown: Boolean) {
|
||||
with(binding.includeContent) {
|
||||
groupEmptyViewItems.visibility = if (isShown) View.GONE else View.VISIBLE
|
||||
groupContentViewItems.visibility = if (isShown) View.VISIBLE else View.GONE
|
||||
}
|
||||
// with(binding.includeContent) {
|
||||
// groupEmptyViewItems.visibility = if (isShown) View.GONE else View.VISIBLE
|
||||
// groupContentViewItems.visibility = if (isShown) View.VISIBLE else View.GONE
|
||||
// }
|
||||
toggleViews(!isShown)
|
||||
}
|
||||
|
||||
|
@ -162,30 +217,6 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO: pull some of this logic into the presenter, particularly the part that deals with ZEC <-> USD price conversion
|
||||
override fun updateBalance(old: Long, new: Long) {
|
||||
val zecValue = new.convertZatoshiToZec()
|
||||
setZecValue(zecValue.toZecString(3))
|
||||
setUsdValue(zecValue.convertZecToUsd(SampleProperties.USD_PER_ZEC).toUsdString())
|
||||
|
||||
onContentRefreshComplete(new)
|
||||
}
|
||||
|
||||
override fun setTransactions(transactions: List<WalletTransaction>) {
|
||||
val recent = if(transactions.size > 12) transactions.subList(0, 12) else transactions
|
||||
with (binding.includeContent.recyclerTransactions) {
|
||||
(adapter as TransactionAdapter).submitList(recent)
|
||||
postDelayed({
|
||||
smoothScrollToPosition(0)
|
||||
}, 100L)
|
||||
}
|
||||
if (recent.size != transactions.size) {
|
||||
binding.includeContent.textTransactionHeaderSeeAll.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun onInitialLoadComplete() {
|
||||
val isEmpty = (binding.includeContent.recyclerTransactions?.adapter?.itemCount ?: 0).let { it == 0 }
|
||||
twig("onInitialLoadComplete and isEmpty == $isEmpty")
|
||||
|
@ -196,25 +227,6 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
setRefreshAnimationPlaying(false).also { twig("refresh false from onInitialLoadComplete") }
|
||||
}
|
||||
|
||||
|
||||
override fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
|
||||
if (activeTransactionMap.isEmpty()) {
|
||||
twig("A.T.: setActiveTransactionsShown(false) because map is empty")
|
||||
setActiveTransactionsShown(false)
|
||||
return
|
||||
}
|
||||
|
||||
val transactions = activeTransactionMap.entries.toTypedArray()
|
||||
// primary is the last one that was inserted
|
||||
val primaryEntry = transactions[transactions.size - 1]
|
||||
updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
|
||||
// TODO: update remaining transactions
|
||||
}
|
||||
|
||||
override fun onCancelledTooLate() {
|
||||
snackbar = snackbar.showOk(view!!, "Oops! It was too late to cancel!")
|
||||
}
|
||||
|
||||
private fun updatePrimaryTransaction(transaction: ActiveTransaction, transactionState: TransactionState) {
|
||||
|
||||
twig("setting transaction state to ${transactionState::class.simpleName}")
|
||||
|
@ -294,7 +306,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
|
||||
|
||||
//
|
||||
// Private View API
|
||||
// Internal View Logic
|
||||
//
|
||||
|
||||
private fun setActiveTransactionsShown(isShown: Boolean, delay: Long = 0L) {
|
||||
|
@ -329,6 +341,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
setColorSchemeColors(R.color.zcashBlack.toAppColor())
|
||||
setProgressBackgroundColorSchemeColor(R.color.zcashYellow.toAppColor())
|
||||
}
|
||||
maxTransactionsShown = calculateMaxTransactions()
|
||||
|
||||
// hide content
|
||||
setContentViewShown(false)
|
||||
|
@ -336,6 +349,10 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
setRefreshAnimationPlaying(true).also { twig("refresh true from init") }
|
||||
}
|
||||
|
||||
private fun calculateMaxTransactions(): Int {
|
||||
return 12 //TODO: measure the screen and get optimal number for this device
|
||||
}
|
||||
|
||||
// initialize the stuff that is temporary and needs to go ASAP
|
||||
private fun initTemp() {
|
||||
|
||||
|
@ -407,14 +424,13 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
* If the balance changes from zero, the wallet is no longer empty so hide the empty view.
|
||||
* But don't do either of these things if the situation has not changed.
|
||||
*/
|
||||
private fun onContentRefreshComplete(value: Long) {
|
||||
val isEmpty = value <= 0L
|
||||
private fun onContentRefreshComplete(isEmpty: Boolean) {
|
||||
// wasEmpty isn't enough info. it must be considered along with whether these views were ever initialized
|
||||
val wasEmpty = binding.includeContent.groupEmptyViewItems.visibility == View.VISIBLE
|
||||
// situation has changed when we weren't initialized but now we have a balance or emptiness has changed
|
||||
val situationHasChanged = !viewsInitialized || (isEmpty != wasEmpty)
|
||||
|
||||
twig("updateEmptyViews called with value: $value initialized: $viewsInitialized isEmpty: $isEmpty wasEmpty: $wasEmpty")
|
||||
twig("onContentRefreshComplete called initialized: $viewsInitialized isEmpty: $isEmpty wasEmpty: $wasEmpty")
|
||||
if (situationHasChanged) {
|
||||
twig("The situation has changed! toggling views!")
|
||||
setContentViewShown(!isEmpty)
|
||||
|
@ -464,6 +480,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
|
||||
private inner class Ticker : Runnable {
|
||||
override fun run() {
|
||||
if (mainActivity == null) return
|
||||
binding.includeContent.recyclerTransactions.apply {
|
||||
if ((adapter?.itemCount ?: 0) > 0) {
|
||||
adapter?.notifyDataSetChanged()
|
||||
|
@ -624,14 +641,11 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class HomeFragmentModule {
|
||||
@ContributesAndroidInjector
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector(modules = [HomePresenterModule::class])
|
||||
abstract fun contributeHomeFragment(): HomeFragment
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun providePresenter(homePresenter: HomePresenter): Presenter
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ abstract class ProgressFragment(
|
|||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
// construct here rather than inject to make scoping easier
|
||||
// so that we reduce the chances of updating progress on the wrong fragment
|
||||
progressPresenter = ProgressPresenter(this, synchronizer)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,15 +23,13 @@ import cash.z.android.wallet.R
|
|||
import cash.z.android.wallet.databinding.FragmentSendBinding
|
||||
import cash.z.android.wallet.extention.*
|
||||
import cash.z.android.wallet.sample.SampleProperties
|
||||
import cash.z.android.wallet.ui.presenter.Presenter
|
||||
import cash.z.android.wallet.ui.presenter.SendPresenter
|
||||
import cash.z.android.wallet.ui.presenter.SendPresenterModule
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Fragment for sending Zcash.
|
||||
|
@ -386,10 +384,6 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
|||
|
||||
@Module
|
||||
abstract class SendFragmentModule {
|
||||
@ContributesAndroidInjector
|
||||
@ContributesAndroidInjector(modules = [SendPresenterModule::class])
|
||||
abstract fun contributeSendFragment(): SendFragment
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun providePresenter(sendPresenter: SendPresenter): Presenter
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentSyncBinding
|
||||
|
@ -43,7 +44,11 @@ class SyncFragment : ProgressFragment(R.id.progress_sync) {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
binding.buttonNext.setOnClickListener {
|
||||
mainActivity?.navController?.navigate(R.id.nav_home_fragment)
|
||||
mainActivity?.navController?.navigate(R.id.action_sync_fragment_to_home_fragment,
|
||||
null,
|
||||
NavOptions.Builder().setPopUpTo(R.id.mobile_navigation, true).build(),
|
||||
null
|
||||
)
|
||||
}
|
||||
binding.progressSync.visibility = View.INVISIBLE
|
||||
binding.textProgressSync.visibility = View.INVISIBLE
|
||||
|
|
|
@ -5,34 +5,16 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.BuildConfig
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentWelcomeBinding
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import android.graphics.Bitmap
|
||||
import android.R.attr.top
|
||||
import android.R.attr.left
|
||||
import android.graphics.RectF
|
||||
import android.os.Parcelable
|
||||
import androidx.core.app.ActivityCompat.setExitSharedElementCallback
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Matrix
|
||||
import android.util.Log
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.BuildConfig
|
||||
import cash.z.android.wallet.ui.presenter.Presenter
|
||||
import cash.z.android.wallet.ui.presenter.ProgressPresenter
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
||||
|
@ -109,7 +91,9 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
|||
private suspend fun onNext() = coroutineScope {
|
||||
if (mainActivity != null) {
|
||||
val isFirstRun = mainActivity!!.synchronizer.isFirstRun()
|
||||
val destination = if (isFirstRun) R.id.nav_firstrun_fragment else R.id.nav_sync_fragment
|
||||
val destination =
|
||||
if (isFirstRun) R.id.action_welcome_fragment_to_firstrun_fragment
|
||||
else R.id.action_welcome_fragment_to_sync_fragment
|
||||
|
||||
// var extras = with(binding) {
|
||||
// listOf(progressWelcome, textProgressWelcome)
|
||||
|
@ -119,10 +103,11 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
|||
val extras = FragmentNavigatorExtras(
|
||||
binding.progressWelcome to binding.progressWelcome.transitionName
|
||||
)
|
||||
|
||||
mainActivity?.navController?.navigate(
|
||||
destination,
|
||||
null,
|
||||
null,
|
||||
NavOptions.Builder().setPopUpTo(R.id.mobile_navigation, true).build(),
|
||||
extras
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package cash.z.android.wallet.ui.presenter
|
||||
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.ui.fragment.HistoryFragment
|
||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.data.Synchronizer
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -16,23 +19,24 @@ import kotlin.coroutines.CoroutineContext
|
|||
class HistoryPresenter @Inject constructor(
|
||||
private val view: HistoryFragment,
|
||||
private var synchronizer: Synchronizer
|
||||
) : Presenter, CoroutineScope {
|
||||
) : Presenter {
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job
|
||||
private var job: Job? = null
|
||||
|
||||
interface HistoryView : PresenterView {
|
||||
fun setTransactions(transactions: List<WalletTransaction>)
|
||||
}
|
||||
|
||||
override suspend fun start() {
|
||||
job?.cancel()
|
||||
job = Job()
|
||||
twig("historyPresenter starting!")
|
||||
launchTransactionBinder(synchronizer.allTransactions())
|
||||
view.launchTransactionBinder(synchronizer.allTransactions())
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
twig("historyPresenter stopping!")
|
||||
job.cancel()
|
||||
job?.cancel()?.also { job = null }
|
||||
}
|
||||
|
||||
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<WalletTransaction>>) = launch {
|
||||
|
@ -58,3 +62,10 @@ class HistoryPresenter @Inject constructor(
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class HistoryPresenterModule {
|
||||
@Binds
|
||||
@FragmentScope
|
||||
abstract fun providePresenter(historyPresenter: HistoryPresenter): Presenter
|
||||
}
|
|
@ -1,24 +1,23 @@
|
|||
package cash.z.android.wallet.ui.presenter
|
||||
|
||||
import android.util.Log
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.ui.fragment.HomeFragment
|
||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.data.*
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomePresenter @Inject constructor(
|
||||
private val view: HomeFragment,
|
||||
private val synchronizer: Synchronizer
|
||||
) : Presenter, CoroutineScope {
|
||||
) : Presenter {
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
|
||||
private var job: Job? = null
|
||||
|
||||
interface HomeView : PresenterView {
|
||||
fun setTransactions(transactions: List<WalletTransaction>)
|
||||
|
@ -28,15 +27,19 @@ class HomePresenter @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun start() {
|
||||
twig("homePresenter starting!")
|
||||
launchBalanceBinder(synchronizer.balance())
|
||||
launchTransactionBinder(synchronizer.allTransactions())
|
||||
launchActiveTransactionMonitor(synchronizer.activeTransactions())
|
||||
job?.cancel()
|
||||
job = Job()
|
||||
twig("homePresenter starting! from ${this.hashCode()}")
|
||||
with(view) {
|
||||
launchBalanceBinder(synchronizer.balance())
|
||||
launchTransactionBinder(synchronizer.allTransactions())
|
||||
launchActiveTransactionMonitor(synchronizer.activeTransactions())
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
twig("homePresenter stopping!")
|
||||
job.cancel()
|
||||
job?.cancel()?.also { job = null }
|
||||
}
|
||||
|
||||
private fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Long>) = launch {
|
||||
|
@ -71,20 +74,20 @@ class HomePresenter @Inject constructor(
|
|||
// View Callbacks on Main Thread
|
||||
//
|
||||
|
||||
private fun bind(old: Long?, new: Long) = onMain {
|
||||
private fun bind(old: Long?, new: Long) {
|
||||
twig("binding balance of $new")
|
||||
view.updateBalance(old ?: 0L, new)
|
||||
}
|
||||
|
||||
|
||||
private fun bind(transactions: List<WalletTransaction>) = onMain {
|
||||
private fun bind(transactions: List<WalletTransaction>) {
|
||||
twig("binding ${transactions.size} walletTransactions")
|
||||
view.setTransactions(transactions.sortedByDescending {
|
||||
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
|
||||
})
|
||||
}
|
||||
|
||||
private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) = onMain {
|
||||
private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
|
||||
twig("binding a.t. map of size ${activeTransactionMap.size}")
|
||||
if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap)
|
||||
}
|
||||
|
@ -97,13 +100,11 @@ class HomePresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onMain(block: () -> Unit) = launch {
|
||||
withContext(Main) {
|
||||
twig("running task on main thread - start ${coroutineContext[Job]} | ${coroutineContext[CoroutineName]}")
|
||||
block()
|
||||
twig("running task on main thread - complete")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Module
|
||||
abstract class HomePresenterModule {
|
||||
@Binds
|
||||
@FragmentScope
|
||||
abstract fun providePresenter(homePresenter: HomePresenter): Presenter
|
||||
}
|
|
@ -12,28 +12,31 @@ import kotlin.coroutines.CoroutineContext
|
|||
class ProgressPresenter @Inject constructor(
|
||||
private val view: ProgressView,
|
||||
private var synchronizer: Synchronizer
|
||||
) : Presenter, CoroutineScope {
|
||||
) : Presenter {
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job
|
||||
private var job: Job? = null
|
||||
|
||||
interface ProgressView : PresenterView {
|
||||
fun showProgress(progress: Int)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
||||
override suspend fun start() {
|
||||
job?.cancel()
|
||||
job = Job()
|
||||
Twig.sprout("ProgressPresenter")
|
||||
twig("starting")
|
||||
launchProgressMonitor(synchronizer.progress())
|
||||
view.launchProgressMonitor(synchronizer.progress())
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
Twig.clip("ProgressPresenter")
|
||||
twig("stopping")
|
||||
job?.cancel()?.also { job = null }
|
||||
}
|
||||
|
||||
private fun CoroutineScope.launchProgressMonitor(channel: ReceiveChannel<Int>) = launch {
|
||||
|
@ -46,7 +49,7 @@ class ProgressPresenter @Inject constructor(
|
|||
twig("progress monitor exiting!")
|
||||
}
|
||||
|
||||
private fun bind(progress: Int) = launch {
|
||||
private fun bind(progress: Int) = view.launch {
|
||||
twig("binding progress of $progress on thread ${Thread.currentThread().name}!")
|
||||
view.showProgress(progress)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.android.wallet.ui.presenter
|
||||
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.extention.toAppString
|
||||
import cash.z.android.wallet.sample.SampleProperties
|
||||
import cash.z.android.wallet.ui.fragment.SendFragment
|
||||
|
@ -9,6 +10,8 @@ import cash.z.wallet.sdk.data.Synchronizer
|
|||
import cash.z.wallet.sdk.data.twig
|
||||
import cash.z.wallet.sdk.data.Twig
|
||||
import cash.z.wallet.sdk.ext.*
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -61,9 +64,9 @@ class SendPresenter @Inject constructor(
|
|||
twig("sendPresenter starting!")
|
||||
// set the currency to zec and update the view, initializing everything to zero
|
||||
inputToggleCurrency()
|
||||
with(view) {
|
||||
balanceJob = launchBalanceBinder(synchronizer.balance())
|
||||
}
|
||||
balanceJob?.cancel()
|
||||
balanceJob = Job()
|
||||
balanceJob = view.launchBalanceBinder(synchronizer.balance())
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
|
@ -348,3 +351,11 @@ class SendPresenter @Inject constructor(
|
|||
val memo: String = ""
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class SendPresenterModule {
|
||||
@Binds
|
||||
@FragmentScope
|
||||
abstract fun providePresenter(sendPresenter: SendPresenter): Presenter
|
||||
}
|
|
@ -1,17 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
|
|
@ -12,12 +12,18 @@
|
|||
tools:layout="@layout/fragment_welcome">
|
||||
<action
|
||||
android:id="@+id/action_welcome_fragment_to_firstrun_fragment"
|
||||
app:popUpTo="@navigation/mobile_navigation"
|
||||
app:popUpToInclusive="true"
|
||||
app:destination="@id/nav_firstrun_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_welcome_fragment_to_sync_fragment"
|
||||
app:popUpTo="@navigation/mobile_navigation"
|
||||
app:popUpToInclusive="true"
|
||||
app:destination="@id/nav_sync_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_welcome_fragment_to_home_fragment"
|
||||
app:popUpTo="@navigation/mobile_navigation"
|
||||
app:popUpToInclusive="true"
|
||||
app:destination="@id/nav_home_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
|
@ -27,6 +33,8 @@
|
|||
tools:layout="@layout/fragment_firstrun">
|
||||
<action
|
||||
android:id="@+id/action_firstrun_fragment_to_sync_fragment"
|
||||
app:popUpTo="@navigation/mobile_navigation"
|
||||
app:popUpToInclusive="true"
|
||||
app:destination="@id/nav_sync_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
|
@ -36,6 +44,8 @@
|
|||
tools:layout="@layout/fragment_sync">
|
||||
<action
|
||||
android:id="@+id/action_sync_fragment_to_home_fragment"
|
||||
app:popUpTo="@navigation/mobile_navigation"
|
||||
app:popUpToInclusive="true"
|
||||
app:destination="@id/nav_home_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
|
|
|
@ -17,7 +17,8 @@ internal object SynchronizerModule {
|
|||
|
||||
// const val MOCK_LOAD_DURATION = 3_000L
|
||||
const val MOCK_LOAD_DURATION = 12_000L
|
||||
const val MOCK_TX_INTERVAL = 20_000L
|
||||
// const val MOCK_TX_INTERVAL = 20_000L
|
||||
const val MOCK_TX_INTERVAL = 5_000L
|
||||
const val MOCK_ACTIVE_TX_STATE_CHANGE_INTERVAL = 7_000L
|
||||
const val MOCK_IS_FIRST_RUN: Boolean = true
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
'dagger': '2.19',
|
||||
'kotlin': '1.3.11',
|
||||
'coroutines': '1.1.0',
|
||||
'navigation': '1.0.0-alpha07'
|
||||
'navigation': '1.0.0-rc01'
|
||||
]
|
||||
ext.deps = [
|
||||
'androidx': [
|
||||
|
|
Loading…
Reference in New Issue