From e79c68fd20ac4af254774063f1f6ee6d271d832a Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Sat, 23 Feb 2019 16:39:29 -0500 Subject: [PATCH] fixes to scoping and job cancellation Utilize fragmentscope in the AndroidInjector subcomponents for fragments and also allow presenters to restart jobs --- .../{ActivityScoped.kt => ActivityScope.kt} | 3 +- .../wallet/di/annotation/ApplicationScope.kt | 7 + ...{ApplicationScoped.kt => FragmentScope.kt} | 3 +- .../di/component/ApplicationComponent.kt | 2 +- .../wallet/ui/activity/MainActivity.kt | 166 +++++++++--------- .../wallet/ui/fragment/FirstrunFragment.kt | 8 +- .../wallet/ui/fragment/HistoryFragment.kt | 10 +- .../wallet/ui/fragment/HomeFragment.kt | 132 +++++++------- .../wallet/ui/fragment/ProgressFragment.kt | 2 + .../wallet/ui/fragment/SendFragment.kt | 10 +- .../wallet/ui/fragment/SyncFragment.kt | 7 +- .../wallet/ui/fragment/WelcomeFragment.kt | 31 +--- .../wallet/ui/presenter/HistoryPresenter.kt | 21 ++- .../wallet/ui/presenter/HomePresenter.kt | 45 ++--- .../wallet/ui/presenter/ProgressPresenter.kt | 13 +- .../wallet/ui/presenter/SendPresenter.kt | 17 +- .../main/res/layout/include_home_header.xml | 15 +- .../main/res/navigation/mobile_navigation.xml | 10 ++ .../wallet/di/module/SynchronizerModule.kt | 3 +- zcash-android-wallet-app/build.gradle | 2 +- 20 files changed, 270 insertions(+), 237 deletions(-) rename zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/{ActivityScoped.kt => ActivityScope.kt} (53%) create mode 100644 zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScope.kt rename zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/{ApplicationScoped.kt => FragmentScope.kt} (53%) diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScoped.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScope.kt similarity index 53% rename from zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScoped.kt rename to zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScope.kt index 0f688c0..86c75db 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScoped.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ActivityScope.kt @@ -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 diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScope.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScope.kt new file mode 100644 index 0000000..3bf9dd2 --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScope.kt @@ -0,0 +1,7 @@ +package cash.z.android.wallet.di.annotation + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class ApplicationScope diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScoped.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/FragmentScope.kt similarity index 53% rename from zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScoped.kt rename to zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/FragmentScope.kt index e437fe0..69fb3a9 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/ApplicationScoped.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/annotation/FragmentScope.kt @@ -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 diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/component/ApplicationComponent.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/component/ApplicationComponent.kt index 6f9a85c..2097408 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/component/ApplicationComponent.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/component/ApplicationComponent.kt @@ -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, diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/activity/MainActivity.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/activity/MainActivity.kt index c2a11dd..f29f52b 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/activity/MainActivity.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/activity/MainActivity.kt @@ -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()?.hideSoftInputFromWindow(binding.navView.windowToken, HIDE_NOT_ALWAYS) } @@ -170,80 +170,80 @@ abstract class MainActivityModule { abstract fun contributeMainActivity(): MainActivity } - -class MultiStartNavigationUI(private val startDestinations: List) { - 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, - 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 - } - } - - } - } -} \ No newline at end of file +// +//class MultiStartNavigationUI(private val startDestinations: List) { +// 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, +// 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 +// } +// } +// +// } +// } +//} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/FirstrunFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/FirstrunFragment.kt index e518c1d..6a84130 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/FirstrunFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/FirstrunFragment.kt @@ -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 diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt index 49c3a16..3d95926 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt @@ -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 } \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt index b3be50f..204d9b4 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt @@ -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) { + 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) { + 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) { - 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) { - 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 } - diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ProgressFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ProgressFragment.kt index 08eeb4a..0274462 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ProgressFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ProgressFragment.kt @@ -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) } diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SendFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SendFragment.kt index d2a22e9..e0ca139 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SendFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SendFragment.kt @@ -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 } diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SyncFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SyncFragment.kt index 95fb17f..4081ac1 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SyncFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/SyncFragment.kt @@ -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 diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/WelcomeFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/WelcomeFragment.kt index ef65976..d035652 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/WelcomeFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/WelcomeFragment.kt @@ -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 ) } diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt index 04a7e8c..f38e4bf 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt @@ -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) } 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>) = launch { @@ -58,3 +62,10 @@ class HistoryPresenter @Inject constructor( } + +@Module +abstract class HistoryPresenterModule { + @Binds + @FragmentScope + abstract fun providePresenter(historyPresenter: HistoryPresenter): Presenter +} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt index 07c6f7b..fb9fbc5 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt @@ -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) @@ -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) = 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) = onMain { + private fun bind(transactions: List) { 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) = onMain { + private fun bind(activeTransactionMap: Map) { 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 +} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/ProgressPresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/ProgressPresenter.kt index ded7739..d1ddc27 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/ProgressPresenter.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/ProgressPresenter.kt @@ -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) = 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) } diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/SendPresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/SendPresenter.kt index fe3eab5..de9b789 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/SendPresenter.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/SendPresenter.kt @@ -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() { @@ -347,4 +350,12 @@ class SendPresenter @Inject constructor( val toAddress: String = "", val memo: String = "" ) +} + + +@Module +abstract class SendPresenterModule { + @Binds + @FragmentScope + abstract fun providePresenter(sendPresenter: SendPresenter): Presenter } \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/res/layout/include_home_header.xml b/zcash-android-wallet-app/app/src/main/res/layout/include_home_header.xml index 696c0b7..edc6f4e 100644 --- a/zcash-android-wallet-app/app/src/main/res/layout/include_home_header.xml +++ b/zcash-android-wallet-app/app/src/main/res/layout/include_home_header.xml @@ -1,17 +1,4 @@ - + diff --git a/zcash-android-wallet-app/app/src/main/res/navigation/mobile_navigation.xml b/zcash-android-wallet-app/app/src/main/res/navigation/mobile_navigation.xml index 1a57c93..aba54ce 100644 --- a/zcash-android-wallet-app/app/src/main/res/navigation/mobile_navigation.xml +++ b/zcash-android-wallet-app/app/src/main/res/navigation/mobile_navigation.xml @@ -12,12 +12,18 @@ tools:layout="@layout/fragment_welcome">