2019-11-26 12:29:16 -08:00
|
|
|
package cash.z.ecc.android.ui
|
|
|
|
|
2020-01-09 23:53:16 -08:00
|
|
|
import android.Manifest
|
2019-11-26 12:29:16 -08:00
|
|
|
import android.content.ClipData
|
|
|
|
import android.content.ClipboardManager
|
|
|
|
import android.content.Context
|
2020-01-09 23:53:16 -08:00
|
|
|
import android.content.pm.PackageManager
|
2019-11-26 12:29:16 -08:00
|
|
|
import android.graphics.Color
|
2019-11-27 06:24:00 -08:00
|
|
|
import android.media.MediaPlayer
|
2020-01-09 23:53:16 -08:00
|
|
|
import android.os.Build
|
2019-11-26 12:29:16 -08:00
|
|
|
import android.os.Bundle
|
2019-11-27 06:24:00 -08:00
|
|
|
import android.os.Vibrator
|
|
|
|
import android.util.Log
|
2019-11-26 12:29:16 -08:00
|
|
|
import android.view.View
|
2019-12-17 13:34:42 -08:00
|
|
|
import android.view.ViewGroup
|
2019-11-26 12:29:16 -08:00
|
|
|
import android.view.WindowManager
|
|
|
|
import android.view.inputmethod.InputMethodManager
|
|
|
|
import android.widget.Toast
|
2020-01-09 08:00:20 -08:00
|
|
|
import androidx.activity.OnBackPressedCallback
|
2020-01-05 21:01:06 -08:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2020-01-09 23:53:16 -08:00
|
|
|
import androidx.core.content.ContextCompat
|
2019-11-26 12:29:16 -08:00
|
|
|
import androidx.core.content.getSystemService
|
2020-01-09 08:00:20 -08:00
|
|
|
import androidx.fragment.app.Fragment
|
2019-12-14 11:39:19 -08:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2019-11-26 12:29:16 -08:00
|
|
|
import androidx.navigation.NavController
|
|
|
|
import androidx.navigation.findNavController
|
|
|
|
import cash.z.ecc.android.R
|
2019-12-14 11:39:19 -08:00
|
|
|
import cash.z.ecc.android.ZcashWalletApp
|
2020-01-05 21:01:06 -08:00
|
|
|
import cash.z.ecc.android.di.component.MainActivitySubcomponent
|
2020-01-06 22:45:24 -08:00
|
|
|
import cash.z.ecc.android.di.component.SynchronizerSubcomponent
|
|
|
|
import cash.z.ecc.android.feedback.Feedback
|
|
|
|
import cash.z.ecc.android.feedback.FeedbackCoordinator
|
|
|
|
import cash.z.ecc.android.feedback.LaunchMetric
|
2019-12-23 11:12:34 -08:00
|
|
|
import cash.z.ecc.android.feedback.Report.NonUserAction.FEEDBACK_STOPPED
|
|
|
|
import cash.z.ecc.android.feedback.Report.NonUserAction.SYNC_START
|
|
|
|
import cash.z.wallet.sdk.Initializer
|
2019-12-17 13:34:42 -08:00
|
|
|
import com.google.android.material.snackbar.Snackbar
|
2019-12-14 11:39:19 -08:00
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import javax.inject.Inject
|
2019-11-26 12:29:16 -08:00
|
|
|
|
|
|
|
|
2020-01-05 21:01:06 -08:00
|
|
|
class MainActivity : AppCompatActivity() {
|
2019-12-14 11:39:19 -08:00
|
|
|
|
|
|
|
@Inject
|
|
|
|
lateinit var feedback: Feedback
|
|
|
|
|
2019-12-17 13:34:42 -08:00
|
|
|
@Inject
|
|
|
|
lateinit var feedbackCoordinator: FeedbackCoordinator
|
|
|
|
|
2019-12-23 11:16:00 -08:00
|
|
|
@Inject
|
2020-01-05 21:01:06 -08:00
|
|
|
lateinit var clipboard: ClipboardManager
|
2019-12-23 11:16:00 -08:00
|
|
|
|
2019-11-27 06:24:00 -08:00
|
|
|
private val mediaPlayer: MediaPlayer = MediaPlayer()
|
2019-12-17 13:34:42 -08:00
|
|
|
private var snackbar: Snackbar? = null
|
|
|
|
|
2020-01-05 21:01:06 -08:00
|
|
|
lateinit var navController: NavController
|
|
|
|
lateinit var component: MainActivitySubcomponent
|
2020-01-06 22:45:24 -08:00
|
|
|
lateinit var synchronizerComponent: SynchronizerSubcomponent
|
2020-01-05 21:01:06 -08:00
|
|
|
|
2020-01-09 23:53:16 -08:00
|
|
|
private val hasCameraPermission
|
|
|
|
get() = ContextCompat.checkSelfPermission(
|
|
|
|
this,
|
|
|
|
Manifest.permission.CAMERA
|
|
|
|
) == PackageManager.PERMISSION_GRANTED
|
2019-12-23 11:16:00 -08:00
|
|
|
|
2019-11-26 12:29:16 -08:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
2020-01-06 22:45:24 -08:00
|
|
|
component = ZcashWalletApp.component.mainActivitySubcomponent().create(this).also {
|
2020-01-05 21:01:06 -08:00
|
|
|
it.inject(this)
|
|
|
|
}
|
2019-11-26 12:29:16 -08:00
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setContentView(R.layout.main_activity)
|
|
|
|
initNavigation()
|
|
|
|
|
2019-12-18 08:09:13 -08:00
|
|
|
window.statusBarColor = Color.TRANSPARENT
|
|
|
|
window.navigationBarColor = Color.TRANSPARENT
|
2019-11-26 12:29:16 -08:00
|
|
|
window.setFlags(
|
|
|
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
|
|
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
|
|
|
)
|
2019-12-18 08:09:13 -08:00
|
|
|
setWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false)
|
|
|
|
setWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, false)
|
2019-12-14 11:39:19 -08:00
|
|
|
|
2019-12-17 13:34:42 -08:00
|
|
|
lifecycleScope.launch {
|
|
|
|
feedback.start()
|
|
|
|
}
|
2019-12-14 11:39:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
// keep track of app launch metrics
|
|
|
|
// (how long does it take the app to open when it is not already in the foreground)
|
|
|
|
ZcashWalletApp.instance.let { app ->
|
|
|
|
if (!app.creationMeasured) {
|
|
|
|
app.creationMeasured = true
|
|
|
|
feedback.report(LaunchMetric())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
lifecycleScope.launch {
|
2019-12-23 11:12:34 -08:00
|
|
|
feedback.report(FEEDBACK_STOPPED)
|
2019-12-14 11:39:19 -08:00
|
|
|
feedback.stop()
|
|
|
|
}
|
|
|
|
super.onDestroy()
|
2019-11-26 12:29:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setWindowFlag(bits: Int, on: Boolean) {
|
|
|
|
val win = window
|
|
|
|
val winParams = win.attributes
|
|
|
|
if (on) {
|
|
|
|
winParams.flags = winParams.flags or bits
|
|
|
|
} else {
|
|
|
|
winParams.flags = winParams.flags and bits.inv()
|
|
|
|
}
|
|
|
|
win.attributes = winParams
|
|
|
|
}
|
2019-11-27 06:24:00 -08:00
|
|
|
|
2019-11-26 12:29:16 -08:00
|
|
|
private fun initNavigation() {
|
|
|
|
navController = findNavController(R.id.nav_host_fragment)
|
|
|
|
navController.addOnDestinationChangedListener { _, _, _ ->
|
|
|
|
// hide the keyboard anytime we change destinations
|
|
|
|
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(
|
|
|
|
this@MainActivity.window.decorView.rootView.windowToken,
|
|
|
|
InputMethodManager.HIDE_NOT_ALWAYS
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-06 22:45:24 -08:00
|
|
|
fun startSync(initializer: Initializer) {
|
|
|
|
synchronizerComponent = ZcashWalletApp.component.synchronizerSubcomponent().create(initializer)
|
2019-12-23 11:12:34 -08:00
|
|
|
feedback.report(SYNC_START)
|
2020-01-06 22:45:24 -08:00
|
|
|
synchronizerComponent.synchronizer().start(lifecycleScope)
|
2019-12-23 11:12:34 -08:00
|
|
|
}
|
|
|
|
|
2019-11-27 06:24:00 -08:00
|
|
|
fun playSound(fileName: String) {
|
|
|
|
mediaPlayer.apply {
|
|
|
|
if (isPlaying) stop()
|
|
|
|
try {
|
|
|
|
reset()
|
|
|
|
assets.openFd(fileName).let { afd ->
|
|
|
|
setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
|
|
|
|
}
|
|
|
|
prepare()
|
|
|
|
start()
|
|
|
|
} catch (t: Throwable) {
|
|
|
|
Log.e("SDK_ERROR", "ERROR: unable to play sound due to $t")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: spruce this up with API 26 stuff
|
|
|
|
fun vibrateSuccess() {
|
|
|
|
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
|
|
|
if (vibrator.hasVibrator()) {
|
|
|
|
vibrator.vibrate(longArrayOf(0, 200, 200, 100, 100, 800), -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-09 08:00:20 -08:00
|
|
|
fun copyAddress(view: View? = null) {
|
2019-12-23 11:16:00 -08:00
|
|
|
lifecycleScope.launch {
|
|
|
|
clipboard.setPrimaryClip(
|
|
|
|
ClipData.newPlainText(
|
|
|
|
"Z-Address",
|
2020-01-06 22:45:24 -08:00
|
|
|
synchronizerComponent.synchronizer().getAddress()
|
2019-12-23 11:16:00 -08:00
|
|
|
)
|
2019-11-26 12:29:16 -08:00
|
|
|
)
|
2019-12-23 11:16:00 -08:00
|
|
|
showMessage("Address copied!", "Sweet")
|
|
|
|
}
|
2019-11-26 12:29:16 -08:00
|
|
|
}
|
|
|
|
|
2020-01-13 16:09:22 -08:00
|
|
|
fun copyText(textToCopy: String, label: String = "zECC Wallet Text") {
|
|
|
|
clipboard.setPrimaryClip(
|
|
|
|
ClipData.newPlainText(label, textToCopy)
|
|
|
|
)
|
|
|
|
showMessage("$label copied!", "Sweet")
|
|
|
|
}
|
|
|
|
|
2020-01-09 08:00:20 -08:00
|
|
|
fun preventBackPress(fragment: Fragment) {
|
|
|
|
onFragmentBackPressed(fragment){}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun onFragmentBackPressed(fragment: Fragment, block: () -> Unit) {
|
|
|
|
onBackPressedDispatcher.addCallback(fragment, object : OnBackPressedCallback(true) {
|
|
|
|
override fun handleOnBackPressed() {
|
|
|
|
block()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-26 12:29:16 -08:00
|
|
|
private fun showMessage(message: String, action: String) {
|
|
|
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
2019-12-17 13:34:42 -08:00
|
|
|
|
|
|
|
fun showSnackbar(message: String, action: String = "OK"): Snackbar {
|
|
|
|
return if (snackbar == null) {
|
|
|
|
val view = findViewById<View>(R.id.main_activity_container)
|
|
|
|
val snacks = Snackbar
|
|
|
|
.make(view, "$message", Snackbar.LENGTH_INDEFINITE)
|
|
|
|
.setAction(action) { /*auto-close*/ }
|
|
|
|
|
|
|
|
val snackBarView = snacks.view as ViewGroup
|
|
|
|
val navigationBarHeight = resources.getDimensionPixelSize(resources.getIdentifier("navigation_bar_height", "dimen", "android"))
|
|
|
|
val params = snackBarView.getChildAt(0).layoutParams as ViewGroup.MarginLayoutParams
|
|
|
|
params.setMargins(
|
|
|
|
params.leftMargin,
|
|
|
|
params.topMargin,
|
|
|
|
params.rightMargin,
|
|
|
|
navigationBarHeight
|
|
|
|
)
|
|
|
|
|
|
|
|
snackBarView.getChildAt(0).setLayoutParams(params)
|
|
|
|
snacks
|
|
|
|
} else {
|
|
|
|
snackbar!!.setText(message).setAction(action) {/*auto-close*/}
|
|
|
|
}.also {
|
|
|
|
if (!it.isShownOrQueued) it.show()
|
|
|
|
}
|
|
|
|
}
|
2020-01-09 23:53:16 -08:00
|
|
|
|
2020-01-13 16:09:22 -08:00
|
|
|
/**
|
|
|
|
* @param popUpToInclusive the destination to remove from the stack before opening the camera.
|
|
|
|
* This only takes effect in the common case where the permission is granted.
|
|
|
|
*/
|
|
|
|
fun maybeOpenScan(popUpToInclusive: Int? = null) {
|
2020-01-09 23:53:16 -08:00
|
|
|
if (hasCameraPermission) {
|
2020-01-13 16:09:22 -08:00
|
|
|
openCamera(popUpToInclusive)
|
2020-01-09 23:53:16 -08:00
|
|
|
} else {
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
requestPermissions(arrayOf(Manifest.permission.CAMERA), 101)
|
|
|
|
} else {
|
|
|
|
onNoCamera()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRequestPermissionsResult(
|
|
|
|
requestCode: Int,
|
|
|
|
permissions: Array<out String>,
|
|
|
|
grantResults: IntArray
|
|
|
|
) {
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
|
|
if (requestCode == 101) {
|
|
|
|
if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
|
|
|
|
openCamera()
|
|
|
|
} else {
|
|
|
|
onNoCamera()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-13 16:09:22 -08:00
|
|
|
private fun openCamera(popUpToInclusive: Int? = null) {
|
|
|
|
navController.navigate(popUpToInclusive ?: R.id.action_global_nav_scan)
|
2020-01-09 23:53:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun onNoCamera() {
|
|
|
|
showSnackbar("Well, this is awkward. You denied permission for the camera.")
|
|
|
|
}
|
2019-11-26 12:29:16 -08:00
|
|
|
}
|