Merge remote-tracking branch 'fekt/main' into main

This commit is contained in:
Jonathan "Duke" Leto
2022-12-04 11:28:10 -08:00
20 changed files with 521 additions and 215 deletions

View File

@@ -143,11 +143,16 @@ dependencies {
implementation Deps.AndroidX.LEGACY implementation Deps.AndroidX.LEGACY
implementation Deps.AndroidX.PAGING implementation Deps.AndroidX.PAGING
implementation Deps.AndroidX.RECYCLER implementation Deps.AndroidX.RECYCLER
implementation Deps.AndroidX.CameraX.CAMERA2
implementation Deps.AndroidX.CameraX.CORE def camerax_version = "1.2.0-rc01"
implementation Deps.AndroidX.CameraX.LIFECYCLE implementation "androidx.camera:camera-core:${camerax_version}"
implementation Deps.AndroidX.CameraX.View.EXT implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation Deps.AndroidX.CameraX.View.VIEW implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
//WindowManager
implementation "androidx.window:window:1.1.0-alpha01"
implementation Deps.AndroidX.Lifecycle.LIFECYCLE_RUNTIME_KTX implementation Deps.AndroidX.Lifecycle.LIFECYCLE_RUNTIME_KTX
implementation Deps.AndroidX.Navigation.FRAGMENT_KTX implementation Deps.AndroidX.Navigation.FRAGMENT_KTX
implementation Deps.AndroidX.Navigation.UI_KTX implementation Deps.AndroidX.Navigation.UI_KTX

View File

@@ -1,5 +1,5 @@
<manifest <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
@@ -11,8 +11,12 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/ZcashTheme"> android:theme="@style/ZcashTheme"
<activity android:name=".ui.MainActivity" android:screenOrientation="portrait"> tools:targetApi="31">
<activity
android:name=".ui.MainActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />

View File

@@ -55,6 +55,38 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
tapped(PROFILE_RESCAN) tapped(PROFILE_RESCAN)
onRescanWallet() onRescanWallet()
} }
// Website
binding.websiteButton.setOnClickListener {
openWebsiteLink()
}
// Telegram
binding.telegramButton.setOnClickListener {
openTelegramLink()
}
// Matrix
binding.matrixButton.setOnClickListener {
openMatrixLink()
}
// Mastodon
binding.mastodonButton.setOnClickListener {
openMastodonLink()
}
// PeerTube
binding.peertubeButton.setOnClickListener {
openPeerTubeLink()
}
// SilentDragon Gitea
binding.textBannerMessage.setOnClickListener {
openGiteaLink()
}
// Add build version
binding.textVersion.text = BuildConfig.VERSION_NAME binding.textVersion.text = BuildConfig.VERSION_NAME
/* /*
@@ -67,33 +99,42 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
onViewDevLogs() onViewDevLogs()
true true
}*/ }*/
binding.iconProfile.setOnLongClickListener {
tapped(AWESOME_OPEN)
onEnterAwesomeMode()
true
}
binding.textBannerMessage.setOnClickListener {
openPlayStoreLink()
}
if (viewModel.isEasterEggTriggered()) {
binding.iconProfile.setImageResource(R.drawable.ic_profile_zebra_02)
}
} }
private fun openPlayStoreLink() { private fun openGiteaLink() {
getString(R.string.play_store_url).takeUnless { it.isBlank() }?.let { url -> getString(R.string.gitea_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url) mainActivity?.onLaunchUrl(url)
} }
} }
private fun onEnterAwesomeMode() { private fun openMastodonLink() {
(context as? MainActivity)?.safeNavigate(R.id.action_nav_profile_to_nav_awesome) getString(R.string.mastodon_url).takeUnless { it.isBlank() }?.let { url ->
?: throw IllegalStateException( mainActivity?.onLaunchUrl(url)
"Cannot navigate from this activity. " + }
"Expected MainActivity but found ${context?.javaClass?.simpleName}" }
)
private fun openMatrixLink() {
getString(R.string.matrix_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url)
}
}
private fun openPeerTubeLink() {
getString(R.string.peertube_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url)
}
}
private fun openTelegramLink() {
getString(R.string.telegram_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url)
}
}
private fun openWebsiteLink() {
getString(R.string.website_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url)
}
} }
override fun onResume() { override fun onResume() {

View File

@@ -1,216 +1,341 @@
package cash.z.ecc.android.ui.scan package cash.z.ecc.android.ui.scan
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.camera.core.* import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowMetricsCalculator
import cash.z.ecc.android.R import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentScanBinding import cash.z.ecc.android.databinding.FragmentScanBinding
import cash.z.ecc.android.ext.onClickNavBack import cash.z.ecc.android.ext.onClickNavBack
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Tap.SCAN_BACK
import cash.z.ecc.android.ui.base.BaseFragment import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.send.SendViewModel import cash.z.ecc.android.ui.send.SendViewModel
import cash.z.ecc.android.util.twig
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class ScanFragment : BaseFragment<FragmentScanBinding>() { class ScanFragment : BaseFragment<FragmentScanBinding>() {
override val screen = Report.Screen.SCAN private var _fragmentCameraBinding: FragmentScanBinding? = null
private val fragmentCameraBinding get() = _fragmentCameraBinding!!
private var displayId: Int = -1
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
private var preview: Preview? = null
private var imageCapture: ImageCapture? = null
private var imageAnalyzer: ImageAnalysis? = null
private var camera: Camera? = null
private var cameraProvider: ProcessCameraProvider? = null
private val viewModel: ScanViewModel by viewModels() private val viewModel: ScanViewModel by viewModels()
private val sendViewModel: SendViewModel by activityViewModels() private val sendViewModel: SendViewModel by activityViewModels()
private lateinit var windowManager: WindowInfoTracker
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider> /** Blocking camera operations are performed using this executor */
private lateinit var cameraExecutor: ExecutorService
private var cameraExecutor: ExecutorService? = null
override fun inflate(inflater: LayoutInflater): FragmentScanBinding = override fun inflate(inflater: LayoutInflater): FragmentScanBinding =
FragmentScanBinding.inflate(inflater) FragmentScanBinding.inflate(inflater)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (cameraExecutor != null) cameraExecutor?.shutdown()
// Initialize our background executor
cameraExecutor = Executors.newSingleThreadExecutor() cameraExecutor = Executors.newSingleThreadExecutor()
binding.backButtonHitArea.onClickNavBack() { tapped(SCAN_BACK) } //Initialize WindowManager to retrieve display metrics
} windowManager = WindowInfoTracker.getOrCreate(view.context)
override fun onCreate(savedInstanceState: Bundle?) { // Wait for the views to be properly laid out
super.onCreate(savedInstanceState) fragmentCameraBinding.viewFinder.post {
if (!allPermissionsGranted()) getRuntimePermissions()
}
override fun onAttach(context: Context) { // Keep track of the display in which this view is attached
super.onAttach(context) displayId = fragmentCameraBinding.viewFinder.display.displayId
cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener( // Set up the camera and its use cases
Runnable { setUpCamera()
bindPreview(cameraProviderFuture.get()) }
},
ContextCompat.getMainExecutor(context) // Initialize back button
) _fragmentCameraBinding?.backButtonHitArea?.onClickNavBack()
} }
override fun onDestroyView() { override fun onDestroyView() {
_fragmentCameraBinding = null
super.onDestroyView() super.onDestroyView()
cameraExecutor?.shutdown()
cameraExecutor = null // Shut down our background executor
cameraExecutor.shutdown()
} }
private fun bindPreview(cameraProvider: ProcessCameraProvider) { override fun onCreateView(
// Most of the code here is adapted from: https://github.com/android/camera-samples/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt inflater: LayoutInflater,
// it's worth keeping tabs on that implementation because they keep making breaking changes to these APIs! container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_fragmentCameraBinding = FragmentScanBinding.inflate(inflater, container, false)
return fragmentCameraBinding.root
}
/** Initialize CameraX, and prepare to bind the camera use cases */
private fun setUpCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener(Runnable {
// CameraProvider
cameraProvider = cameraProviderFuture.get()
// Select lensFacing depending on the available cameras
lensFacing = when {
hasBackCamera() -> CameraSelector.LENS_FACING_BACK
hasFrontCamera() -> CameraSelector.LENS_FACING_FRONT
else -> throw IllegalStateException("Back and front camera are unavailable")
}
// Build and bind the camera use cases
bindCameraUseCases()
}, ContextCompat.getMainExecutor(requireContext()))
}
/** Declare and bind preview, capture and analysis use cases */
private fun bindCameraUseCases() {
// Get screen metrics used to setup camera for full screen resolution // Get screen metrics used to setup camera for full screen resolution
val metrics = DisplayMetrics().also { binding.preview.display.getRealMetrics(it) } /*
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels) val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(requireActivity()).bounds
val rotation = binding.preview.display.rotation Log.d("SilentDragon", "Screen metrics: ${metrics.width()} x ${metrics.height()}")
val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())
*/
val preview = // Hardcode to square for now otherwise scanning doesn't work
Preview.Builder().setTargetName("Preview").setTargetAspectRatio(screenAspectRatio) val screenAspectRatio = aspectRatio(1, 1)
.setTargetRotation(rotation).build() Log.d("SilentDragon", "Preview aspect ratio: $screenAspectRatio")
val cameraSelector = CameraSelector.Builder() val rotation = fragmentCameraBinding.viewFinder.display.rotation
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
// CameraProvider
val cameraProvider = cameraProvider
?: throw IllegalStateException("Camera initialization failed.")
// CameraSelector
val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
// Preview
preview = Preview.Builder()
// We request aspect ratio but no resolution
.setTargetAspectRatio(screenAspectRatio)
// Set initial target rotation
.setTargetRotation(rotation)
.build() .build()
val imageAnalysis = ImageAnalysis.Builder().setTargetAspectRatio(screenAspectRatio) // ImageCapture
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
// We request aspect ratio but no resolution to match preview config, but letting
// CameraX optimize for whatever specific resolution best fits our use cases
.setTargetAspectRatio(screenAspectRatio)
// Set initial target rotation, we will have to call this again if rotation changes
// during the lifecycle of this use case
.setTargetRotation(rotation)
.build()
// ImageAnalysis
imageAnalyzer = ImageAnalysis.Builder()
// We request aspect ratio but no resolution
.setTargetAspectRatio(screenAspectRatio)
// Set initial target rotation, we will have to call this again if rotation changes
// during the lifecycle of this use case
.setTargetRotation(rotation) .setTargetRotation(rotation)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build() .build()
imageAnalysis.setAnalyzer(
cameraExecutor!!, // The analyzer can then be assigned to the instance
QrAnalyzer { q, i -> .also {
onQrScanned(q, i) it.setAnalyzer(cameraExecutor,
QrAnalyzer { q, i ->
onQrScanned(q, i)
}
)
} }
)
// Must unbind the use-cases before rebinding them // Must unbind the use-cases before rebinding them
cameraProvider.unbindAll() cameraProvider.unbindAll()
if (camera != null) {
// Must remove observers from the previous camera instance
removeCameraStateObservers(camera!!.cameraInfo)
}
try { try {
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis) // A variable number of use-cases can be passed here -
preview.setSurfaceProvider(binding.preview.surfaceProvider) // camera provides access to CameraControl & CameraInfo
} catch (t: Throwable) { camera = cameraProvider.bindToLifecycle(
// TODO: consider bubbling this up to the user this, cameraSelector, preview, imageCapture, imageAnalyzer)
mainActivity?.feedback?.report(t)
twig("Error while opening the camera: $t") // Attach the viewfinder's surface provider to preview use case
preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
observeCameraState(camera?.cameraInfo!!)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}
private fun removeCameraStateObservers(cameraInfo: CameraInfo) {
cameraInfo.cameraState.removeObservers(viewLifecycleOwner)
}
private fun observeCameraState(cameraInfo: CameraInfo) {
cameraInfo.cameraState.observe(viewLifecycleOwner) { cameraState ->
run {
when (cameraState.type) {
CameraState.Type.PENDING_OPEN -> {
// Ask the user to close other camera apps
Toast.makeText(context,
"CameraState: Pending Open",
Toast.LENGTH_SHORT).show()
}
CameraState.Type.OPENING -> {
// Show the Camera UI
Toast.makeText(context,
"CameraState: Opening",
Toast.LENGTH_SHORT).show()
}
CameraState.Type.OPEN -> {
// Setup Camera resources and begin processing
Toast.makeText(context,
"CameraState: Open",
Toast.LENGTH_SHORT).show()
}
CameraState.Type.CLOSING -> {
// Close camera UI
Toast.makeText(context,
"CameraState: Closing",
Toast.LENGTH_SHORT).show()
}
CameraState.Type.CLOSED -> {
// Free camera resources
Toast.makeText(context,
"CameraState: Closed",
Toast.LENGTH_SHORT).show()
}
}
}
cameraState.error?.let { error ->
when (error.code) {
// Open errors
CameraState.ERROR_STREAM_CONFIG -> {
// Make sure to setup the use cases properly
Toast.makeText(context,
"Stream config error",
Toast.LENGTH_SHORT).show()
}
// Opening errors
CameraState.ERROR_CAMERA_IN_USE -> {
// Close the camera or ask user to close another camera app that's using the
// camera
Toast.makeText(context,
"Camera in use",
Toast.LENGTH_SHORT).show()
}
CameraState.ERROR_MAX_CAMERAS_IN_USE -> {
// Close another open camera in the app, or ask the user to close another
// camera app that's using the camera
Toast.makeText(context,
"Max cameras in use",
Toast.LENGTH_SHORT).show()
}
CameraState.ERROR_OTHER_RECOVERABLE_ERROR -> {
Toast.makeText(context,
"Other recoverable error",
Toast.LENGTH_SHORT).show()
}
// Closing errors
CameraState.ERROR_CAMERA_DISABLED -> {
// Ask the user to enable the device's cameras
Toast.makeText(context,
"Camera disabled",
Toast.LENGTH_SHORT).show()
}
CameraState.ERROR_CAMERA_FATAL_ERROR -> {
// Ask the user to reboot the device to restore camera function
Toast.makeText(context,
"Fatal error",
Toast.LENGTH_SHORT).show()
}
// Closed errors
CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED -> {
// Ask the user to disable the "Do Not Disturb" mode, then reopen the camera
Toast.makeText(context,
"Do not disturb mode enabled",
Toast.LENGTH_SHORT).show()
}
}
}
} }
} }
/** /**
* Adapted from: https://github.com/android/camera-samples/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt#L350 * [androidx.camera.core.ImageAnalysis.Builder] requires enum value of
* [androidx.camera.core.AspectRatio]. Currently it has values of 4:3 & 16:9.
*
* Detecting the most suitable ratio for dimensions provided in @params by counting absolute
* of preview ratio to one of the provided values.
*
* @param width - preview width
* @param height - preview height
* @return suitable aspect ratio
*/ */
private fun aspectRatio(width: Int, height: Int): Int { private fun aspectRatio(width: Int, height: Int): Int {
val previewRatio = kotlin.math.max(width, height).toDouble() / kotlin.math.min( val previewRatio = max(width, height).toDouble() / min(width, height)
width, if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
height
)
if (kotlin.math.abs(previewRatio - (4.0 / 3.0))
<= kotlin.math.abs(previewRatio - (16.0 / 9.0))
) {
return AspectRatio.RATIO_4_3 return AspectRatio.RATIO_4_3
} }
return AspectRatio.RATIO_16_9 return AspectRatio.RATIO_16_9
} }
/** Returns true if the device has an available back camera. False otherwise */
private fun hasBackCamera(): Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false
}
/** Returns true if the device has an available front camera. False otherwise */
private fun hasFrontCamera(): Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false
}
companion object {
private const val TAG = "SilentDragon"
private const val RATIO_4_3_VALUE = 4.0 / 3.0
private const val RATIO_16_9_VALUE = 16.0 / 9.0
}
private fun onQrScanned(qrContent: String, image: ImageProxy) { private fun onQrScanned(qrContent: String, image: ImageProxy) {
//Log.d("SilentDragon", "QR scanned: $qrContent")
resumedScope.launch { resumedScope.launch {
val parsed = viewModel.parse(qrContent) val parsed = viewModel.parse(qrContent)
if (parsed == null) { if (parsed == null) {
val network = viewModel.networkName val network = viewModel.networkName
binding.textScanError.text = getString(R.string.scan_invalid_address, network, qrContent) _fragmentCameraBinding?.textScanError?.text =
getString(R.string.scan_invalid_address, network, qrContent)
image.close() image.close()
} else { /* continue scanning*/ } else { /* continue scanning*/
binding.textScanError.text = "" _fragmentCameraBinding?.textScanError?.text = ""
sendViewModel.toAddress = parsed sendViewModel.toAddress = parsed
mainActivity?.safeNavigate(R.id.action_nav_scan_to_nav_send) mainActivity?.safeNavigate(R.id.action_nav_scan_to_nav_send)
} }
} }
} }
// private fun updateOverlay(detectedObjects: DetectedObjects) {
// if (detectedObjects.objects.isEmpty()) {
// return
// }
//
// overlay.setSize(detectedObjects.imageWidth, detectedObjects.imageHeight)
// val list = mutableListOf<BoxData>()
// for (obj in detectedObjects.objects) {
// val box = obj.boundingBox
// val name = "${categoryNames[obj.classificationCategory]}"
// val confidence =
// if (obj.classificationCategory != FirebaseVisionObject.CATEGORY_UNKNOWN) {
// val confidence: Int = obj.classificationConfidence!!.times(100).toInt()
// "$confidence%"
// } else {
// ""
// }
// list.add(BoxData("$name $confidence", box))
// }
// overlay.set(list)
// }
//
// Permissions
//
private val requiredPermissions: Array<String?>
get() {
return try {
val info = mainActivity?.packageManager
?.getPackageInfo(mainActivity?.packageName ?: "", PackageManager.GET_PERMISSIONS)
val ps = info?.requestedPermissions
if (ps != null && ps.isNotEmpty()) {
ps
} else {
arrayOfNulls(0)
}
} catch (e: Exception) {
arrayOfNulls(0)
}
}
private fun allPermissionsGranted(): Boolean {
for (permission in requiredPermissions) {
if (!isPermissionGranted(mainActivity!!, permission!!)) {
return false
}
}
return true
}
private fun getRuntimePermissions() {
val allNeededPermissions = arrayListOf<String>()
for (permission in requiredPermissions) {
if (!isPermissionGranted(mainActivity!!, permission!!)) {
allNeededPermissions.add(permission)
}
}
if (allNeededPermissions.isNotEmpty()) {
requestPermissions(allNeededPermissions.toTypedArray(), CAMERA_PERMISSION_REQUEST)
}
}
companion object {
private const val CAMERA_PERMISSION_REQUEST = 1002
private fun isPermissionGranted(context: Context, permission: String): Boolean {
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
}
}
} }

View File

@@ -12,9 +12,9 @@ class ScanViewModel : ViewModel() {
val networkName get() = synchronizer.network.networkName val networkName get() = synchronizer.network.networkName
suspend fun parse(qrCode: String): String? { suspend fun parse(qrCode: String): String? {
// temporary parse code to allow both plain addresses and those that start with zcash: // temporary parse code to allow both plain addresses and those that start with hush:
// TODO: replace with more robust ZIP-321 handling of QR codes // TODO: replace with more robust ZIP-321 handling of QR codes
val address = if (qrCode.startsWith("zcash:")) { val address = if (qrCode.startsWith("hush:")) {
qrCode.substring(6, qrCode.indexOf("?").takeUnless { it == -1 } ?: qrCode.length) qrCode.substring(6, qrCode.indexOf("?").takeUnless { it == -1 } ?: qrCode.length)
} else { } else {
qrCode qrCode

View File

@@ -63,6 +63,9 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
binding.buttonPositive.setOnClickListener { binding.buttonPositive.setOnClickListener {
onEnterWallet().also { if (hasBackUp) tapped(BACKUP_DONE) else tapped(BACKUP_VERIFY) } onEnterWallet().also { if (hasBackUp) tapped(BACKUP_DONE) else tapped(BACKUP_VERIFY) }
} }
binding.buttonSeedphraseMoreInfo.setOnClickListener {
openSeedPhraseMoreInfoLink()
}
if (hasBackUp) { if (hasBackUp) {
binding.buttonPositive.text = getString(R.string.backup_button_done) binding.buttonPositive.text = getString(R.string.backup_button_done)
} }
@@ -155,4 +158,10 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
result result
} }
} }
private fun openSeedPhraseMoreInfoLink() {
getString(R.string.seedphrase_more_info_url).takeUnless { it.isBlank() }?.let { url ->
mainActivity?.onLaunchUrl(url)
}
}
} }

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="448"
android:viewportHeight="512"
android:width="448dp"
android:height="512dp">
<path
android:pathData="M433 179.11c0 -97.2 -63.71 -125.7 -63.71 -125.7 -62.52 -28.7 -228.56 -28.4 -290.48 0 0 0 -63.72 28.5 -63.72 125.7 0 115.7 -6.6 259.4 105.63 289.1 40.51 10.7 75.32 13 103.33 11.4 50.81 -2.8 79.32 -18.1 79.32 -18.1l-1.7 -36.9s-36.31 11.4 -77.12 10.1c-40.41 -1.4 -83 -4.4 -89.63 -54a102.54 102.54 0 0 1 -0.9 -13.9c85.63 20.9 158.65 9.1 178.75 6.7 56.12 -6.7 105 -41.3 111.23 -72.9 9.8 -49.8 9 -121.5 9 -121.5zm-75.12 125.2h-46.63v-114.2c0 -49.7 -64 -51.6 -64 6.9v62.5h-46.33V197c0 -58.5 -64 -56.6 -64 -6.9v114.2H90.19c0 -122.1 -5.2 -147.9 18.41 -175 25.9 -28.9 79.82 -30.8 103.83 6.1l11.6 19.5 11.6 -19.5c24.11 -37.1 78.12 -34.8 103.83 -6.1 23.71 27.3 18.4 53 18.4 175z"
android:fillColor="#000000" />
</vector>

View File

@@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="64"
android:viewportHeight="64"
android:width="64dp"
android:height="64dp">
<path
android:pathData="M49.46 42.2h-5.32c-0.178 0 -0.323 -0.145 -0.323 -0.323V30.91l-0.074 -1.808c-0.047 -0.53 -0.173 -0.992 -0.376 -1.376 -0.194 -0.367 -0.487 -0.664 -0.868 -0.883s-0.93 -0.332 -1.62 -0.332 -1.238 0.13 -1.647 0.382 -0.743 0.597 -0.976 1.01a4.21 4.21 0 0 0 -0.486 1.462c-0.085 0.567 -0.128 1.15 -0.128 1.732v10.79c0 0.178 -0.145 0.323 -0.323 0.323H32c-0.178 0 -0.323 -0.145 -0.323 -0.323V31.02l-0.037 -1.69c-0.024 -0.524 -0.124 -1.013 -0.297 -1.45 -0.164 -0.415 -0.43 -0.74 -0.814 -0.992s-0.972 -0.378 -1.752 -0.378c-0.22 0 -0.527 0.053 -0.908 0.157 -0.368 0.1 -0.732 0.294 -1.08 0.577s-0.65 0.694 -0.904 1.235 -0.382 1.27 -0.382 2.167v11.24c0 0.178 -0.144 0.323 -0.323 0.323h-5.32c-0.178 0 -0.323 -0.145 -0.323 -0.323V22.515c0 -0.178 0.145 -0.322 0.323 -0.322h5.02c0.178 0 0.323 0.145 0.323 0.322V24.3c0.618 -0.726 1.33 -1.315 2.125 -1.757 1.032 -0.574 2.225 -0.865 3.548 -0.865 1.265 0 2.44 0.25 3.5 0.743 0.934 0.44 1.68 1.17 2.224 2.18 0.556 -0.703 1.263 -1.34 2.108 -1.895 1.036 -0.682 2.274 -1.028 3.68 -1.028 1.048 0 2.036 0.13 2.937 0.387 0.917 0.263 1.715 0.69 2.373 1.267s1.18 1.348 1.548 2.278c0.363 0.922 0.547 2.04 0.547 3.323v12.964c0 0.178 -0.145 0.323 -0.323 0.323z"
android:fillColor="#FFFFFF"
android:fillAlpha="0.5" />
<path
android:pathData="M24.88 22.515v2.623h0.075c0.7 -0.998 1.542 -1.774 2.53 -2.323s2.117 -0.824 3.39 -0.824c1.224 0 2.342 0.238 3.353 0.712s1.78 1.31 2.305 2.51c0.574 -0.85 1.355 -1.6 2.342 -2.248s2.154 -0.974 3.504 -0.974c1.024 0 1.973 0.125 2.848 0.375s1.623 0.65 2.248 1.2 1.11 1.268 1.462 2.154 0.525 1.955 0.525 3.204v12.964h-5.32V30.91l-0.075 -1.836c-0.05 -0.574 -0.187 -1.073 -0.412 -1.5s-0.556 -0.762 -0.993 -1.012 -1.03 -0.374 -1.78 -0.374 -1.355 0.145 -1.817 0.43 -0.824 0.663 -1.087 1.124 -0.437 0.987 -0.524 1.574a12 12 0 0 0 -0.131 1.78v10.79H32V31.022l-0.037 -1.705c-0.025 -0.562 -0.13 -1.08 -0.32 -1.556s-0.5 -0.855 -0.937 -1.143 -1.08 -0.43 -1.93 -0.43c-0.25 0 -0.58 0.056 -0.993 0.17a3.3 3.3 0 0 0 -1.199 0.637c-0.388 0.313 -0.718 0.762 -0.993 1.35s-0.412 1.355 -0.412 2.304v11.24h-5.32V22.515z"
android:fillColor="#FFFFFF"
android:fillAlpha="0.5" />
<path
android:pathData="M1.432 6.084v51.833h3.73v1.244H0V4.84h5.162v1.243zm20.788 16.43v2.623h0.075c0.7 -0.998 1.542 -1.774 2.53 -2.323s2.117 -0.824 3.4 -0.824c1.224 0 2.342 0.238 3.353 0.712s1.78 1.3 2.305 2.5c0.574 -0.85 1.355 -1.6 2.342 -2.248s2.154 -0.974 3.504 -0.974c1.024 0 1.973 0.125 2.848 0.375s1.623 0.65 2.248 1.2 1.1 1.268 1.462 2.154 0.525 1.955 0.525 3.204v12.964h-5.32V30.9l-0.075 -1.836c-0.05 -0.574 -0.187 -1.073 -0.412 -1.5s-0.556 -0.762 -0.993 -1.012 -1.03 -0.374 -1.78 -0.374 -1.355 0.145 -1.817 0.43a3.12 3.12 0 0 0 -1.087 1.124c-0.263 0.46 -0.437 0.987 -0.524 1.574a12 12 0 0 0 -0.131 1.78v10.8h-5.32V31.022l-0.037 -1.705c-0.025 -0.562 -0.13 -1.08 -0.32 -1.556s-0.5 -0.855 -0.937 -1.143 -1.08 -0.43 -1.93 -0.43c-0.25 0 -0.58 0.056 -0.993 0.17a3.3 3.3 0 0 0 -1.199 0.637c-0.388 0.313 -0.718 0.762 -0.993 1.35s-0.412 1.355 -0.412 2.304v11.24H17.2V22.515zm40.348 35.402V6.084h-3.73V4.84H64v54.32h-5.162v-1.244z"
android:fillColor="#000000" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="24"
android:viewportHeight="24"
android:width="24dp"
android:height="24dp">
<path
android:pathData="M12 6.545v10.91L20.727 12M3.273 12v12L12 17.455M3.273 0v12L12 6.545"
android:fillColor="#000000" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="496"
android:viewportHeight="512"
android:width="496dp"
android:height="512dp">
<path
android:pathData="M248 8C111.033 8 0 119.033 0 256S111.033 504 248 504 496 392.967 496 256 384.967 8 248 8ZM362.952 176.66c-3.732 39.215 -19.881 134.378 -28.1 178.3 -3.476 18.584 -10.322 24.816 -16.948 25.425 -14.4 1.326 -25.338 -9.517 -39.287 -18.661 -21.827 -14.308 -34.158 -23.215 -55.346 -37.177 -24.485 -16.135 -8.612 -25 5.342 -39.5 3.652 -3.793 67.107 -61.51 68.335 -66.746 0.153 -0.655 0.3 -3.1 -1.154 -4.384s-3.59 -0.849 -5.135 -0.5q-3.283 0.746 -104.608 69.142 -14.845 10.194 -26.894 9.934c-8.855 -0.191 -25.888 -5.006 -38.551 -9.123 -15.531 -5.048 -27.875 -7.717 -26.8 -16.291q0.84 -6.7 18.45 -13.7 108.446 -47.248 144.628 -62.3c68.872 -28.647 83.183 -33.623 92.511 -33.789 2.052 -0.034 6.639 0.474 9.61 2.885a10.452 10.452 0 0 1 3.53 6.716A43.765 43.765 0 0 1 362.952 176.66Z"
android:fillColor="#000000" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="512"
android:viewportHeight="512"
android:width="512dp"
android:height="512dp">
<path
android:pathData="M352 256c0 22.2 -1.2 43.6 -3.3 64H163.3c-2.2 -20.4 -3.3 -41.8 -3.3 -64s1.2 -43.6 3.3 -64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8 -64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5 -8.1 64H380.8c2.1 -20.6 3.2 -42 3.2 -64s-1.1 -43.4 -3.2 -64zm112.6 -32H376.7c-10 -63.9 -29.8 -117.4 -55.3 -151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1 -36.4 15.5 -68.6 27 -94.7c10.5 -23.6 22.2 -40.7 33.5 -51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 21 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6 -3.2 42 -3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8 -43.5 8.1 -64zM194.7 446.6c-11.6 -26 -20.9 -58.2 -27 -94.6H344.3c-6.1 36.4 -15.5 68.6 -27 94.6c-10.5 23.6 -22.2 40.7 -33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6 -3.2 -27.8 -13.8c-11.3 -10.8 -23 -27.9 -33.5 -51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1 -93.6 130.9 -171.9 151.6c25.5 -34.2 45.2 -87.7 55.3 -151.6H493.4z"
android:fillColor="#000000" />
</vector>

View File

@@ -12,7 +12,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintGuide_percent="0.667" /> app:layout_constraintGuide_percent="0.75" />
<!-- Address parts --> <!-- Address parts -->
@@ -363,14 +363,15 @@ text_address_part_3, text_address_part_6, text_address_part_9, text_address_part
<!-- Choose release names from here https://en.wikipedia.org/wiki/List_of_woods --> <!-- Choose release names from here https://en.wikipedia.org/wiki/List_of_woods -->
<TextView <TextView
android:id="@+id/text_message" android:id="@+id/text_message"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:gravity="center" android:gravity="center"
android:paddingStart="32dp" android:paddingStart="32dp"
android:paddingEnd="32dp" android:paddingEnd="32dp"
android:text="@string/backup_instruction_store_words" android:text="@string/backup_instruction_store_words"
android:textColor="@color/zcashWhite" android:textColor="@color/zcashWhite"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/guideline_buttons" app:layout_constraintBottom_toTopOf="@id/guideline_buttons"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -380,7 +381,19 @@ text_address_part_3, text_address_part_6, text_address_part_9, text_address_part
android:id="@+id/button_positive" android:id="@+id/button_positive"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="320dp"
android:text="@string/backup_button_primary" android:text="@string/backup_button_primary"
android:textColor="@color/text_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_seedphrase_more_info" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_seedphrase_more_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="320dp"
android:text="@string/backup_button_more_info"
android:textColor="@color/text_dark" android:textColor="@color/text_dark"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@@ -86,16 +86,16 @@
android:id="@+id/icon_profile" android:id="@+id/icon_profile"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:contentDescription="@string/content_description_profile_zebra"
android:elevation="6dp" android:elevation="6dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="H,1:1" app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.212" app:layout_constraintVertical_bias="0.137"
app:layout_constraintWidth_percent="0.4" app:layout_constraintWidth_percent="0.4"
app:srcCompat="@drawable/ic_profile_zebra_01" app:srcCompat="@drawable/ic_profile_zebra_01" />
android:contentDescription="@string/content_description_profile_zebra" />
<View <View
android:id="@+id/hit_area_settings" android:id="@+id/hit_area_settings"
@@ -148,45 +148,28 @@
android:id="@+id/hit_area_address" android:id="@+id/hit_area_address"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginBottom="16dp"
android:onClick="copyAddress" android:onClick="copyAddress"
app:layout_constraintBottom_toTopOf="@id/button_feedback" app:layout_constraintBottom_toTopOf="@id/button_backup"
app:layout_constraintEnd_toEndOf="@id/guideline_content_end" app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
app:layout_constraintStart_toStartOf="@id/text_address" app:layout_constraintStart_toStartOf="@id/text_address"
app:layout_constraintTop_toTopOf="@id/text_username" app:layout_constraintTop_toTopOf="@id/text_username"
tools:background="@color/spacer" /> tools:background="@color/spacer" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_feedback"
style="@style/Zcash.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp"
android:text="@string/profile_send_feedback"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="#000000"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
app:layout_constraintTop_toBottomOf="@id/text_address"
app:layout_constraintVertical_bias="0.1"
tools:visibility="invisible" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_backup" android:id="@+id/button_backup"
style="@style/Zcash.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/Zcash.Button.OutlinedButton" android:layout_marginTop="24dp"
android:gravity="center" android:gravity="center"
android:padding="12dp" android:padding="12dp"
android:text="@string/profile_backup_wallet" android:text="@string/profile_backup_wallet"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/text_light" android:textColor="@color/text_light"
app:layout_constraintEnd_toEndOf="@id/guideline_content_end" app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@id/guideline_content_start" app:layout_constraintStart_toStartOf="@id/guideline_content_start"
app:layout_constraintTop_toBottomOf="@id/button_feedback" /> app:layout_constraintTop_toBottomOf="@id/text_address" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_rescan" android:id="@+id/button_rescan"
@@ -210,7 +193,7 @@
android:id="@+id/text_banner_message" android:id="@+id/text_banner_message"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="64dp" android:layout_marginBottom="128dp"
android:background="@drawable/background_banner" android:background="@drawable/background_banner"
android:elevation="6dp" android:elevation="6dp"
android:paddingStart="16dp" android:paddingStart="16dp"
@@ -219,10 +202,10 @@
android:text="@string/profile_ecc_wallet" android:text="@string/profile_ecc_wallet"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/selector_button_text_light" android:textColor="@color/selector_button_text_light"
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
tools:ignore="RtlSymmetry" /> tools:ignore="RtlSymmetry" />
<TextView <TextView
@@ -237,4 +220,73 @@
app:layout_constraintEnd_toEndOf="@id/text_banner_message" app:layout_constraintEnd_toEndOf="@id/text_banner_message"
tools:text="v1.0.0-alpha05" /> tools:text="v1.0.0-alpha05" />
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="© 2016-2022 The Hush Developers\nAll rights reserved. Licensed under GPLv3"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_banner_message" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="57dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:orientation="horizontal"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@+id/text_banner_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_rescan">
<ImageButton
android:id="@+id/websiteButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_weight="1"
android:scaleType="fitCenter"
app:tint="#333333"
app:srcCompat="@drawable/ic_website" />
<ImageButton
android:id="@+id/telegramButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_weight="1"
android:scaleType="fitCenter"
app:tint="#333333"
app:srcCompat="@drawable/ic_telegram" />
<ImageButton
android:id="@+id/matrixButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_weight="1"
android:scaleType="fitCenter"
app:tint="#333333"
app:srcCompat="@drawable/ic_matrix" />
<ImageButton
android:id="@+id/mastodonButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_weight="1"
android:scaleType="fitCenter"
app:tint="#333333"
app:srcCompat="@drawable/ic_mastodon" />
<ImageButton
android:id="@+id/peertubeButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_weight="1"
android:scaleType="fitCenter"
app:tint="#333333"
app:srcCompat="@drawable/ic_peertube" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -46,7 +46,7 @@
tools:background="@color/zcashRed" /> tools:background="@color/zcashRed" />
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
android:id="@+id/preview" android:id="@+id/viewFinder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@@ -282,6 +282,10 @@
android:id="@+id/nav_backup" android:id="@+id/nav_backup"
android:name="cash.z.ecc.android.ui.setup.BackupFragment" android:name="cash.z.ecc.android.ui.setup.BackupFragment"
tools:layout="@layout/fragment_backup" > tools:layout="@layout/fragment_backup" >
<activity
android:id="@+id/button_seedphrase_more_info"
app:action="android.intent.action.VIEW"
app:data="@string/seedphrase_more_info_url" />
</fragment> </fragment>
<fragment <fragment

View File

@@ -5,7 +5,8 @@
<!-- Manual --> <!-- Manual -->
<string name="custom_translation_verify">Verify</string> <string name="custom_translation_verify">I have written down my seed phrase and stored securely</string>
<string name="custom_translation_seedphrase_more_info">More information</string>
<string name="custom_translation_birthday">Birthday Height: %1$,d</string> <string name="custom_translation_birthday">Birthday Height: %1$,d</string>
<string name="custom_translation_expecting">Expecting</string> <string name="custom_translation_expecting">Expecting</string>
<string name="custom_translation_syncing">Downloading…%1$d%%</string> <string name="custom_translation_syncing">Downloading…%1$d%%</string>

View File

@@ -6,9 +6,9 @@
<!-- ======================================================================================= --> <!-- ======================================================================================= -->
<string name="missing_cancel">Cancel</string> <string name="missing_cancel">Cancel</string>
<string name="missing_backup_instruction_store_words">Store these backup words securely.</string> <string name="missing_backup_instruction_store_words">Write down these 24 words on paper. Make sure you write them in the correct order and don\'t misspell any words. Make a copy of that paper and store them securely in at least 2 physical locations</string>
<string name="missing_backup_slogan">empowering\neveryone\nwith\neconomic\nfreedom</string> <string name="missing_backup_slogan">empowering\neveryone\nwith\neconomic\nfreedom</string>
<string name="missing_backup_verification_not_implemented">Backup verification coming soon!</string> <string name="missing_backup_verification_not_implemented">You may lose all your funds if you didn\'t actually backup! Be smart and responsible!</string>
<!-- --> <!-- -->
<!-- SCREENS --> <!-- SCREENS -->

View File

@@ -13,6 +13,7 @@
<!-- Screen: Backup --> <!-- Screen: Backup -->
<string name="backup_button_done">@string/translated_button_done</string> <string name="backup_button_done">@string/translated_button_done</string>
<string name="backup_button_primary">@string/custom_translation_verify</string> <string name="backup_button_primary">@string/custom_translation_verify</string>
<string name="backup_button_more_info">@string/custom_translation_seedphrase_more_info</string>
<string name="backup_format_birthday_height">@string/custom_translation_birthday</string> <string name="backup_format_birthday_height">@string/custom_translation_birthday</string>
<string name="backup_instruction_store_words">@string/missing_backup_instruction_store_words</string> <string name="backup_instruction_store_words">@string/missing_backup_instruction_store_words</string>
<string name="backup_slogan">@string/missing_backup_slogan</string> <string name="backup_slogan">@string/missing_backup_slogan</string>

View File

@@ -1,5 +1,11 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="api_block_explorer">https://explorer.hush.is/tx/%1$s</string> <string name="api_block_explorer">https://explorer.hush.is/tx/%1$s</string>
<string name="gitea_url" translatable="false">https://git.hush.is/hush/SilentDragonAndroid/releases</string>
<string name="matrix_url" translatable="false">https://hush.is/matrix</string>
<string name="mastodon_url" translatable="false">https://hush.is/mastodon</string>
<string name="peertube_url" translatable="false">https://videos.hush.is/</string>
<string name="seedphrase_more_info_url">https://hush.is/seedphrase</string>
<string name="symbol" translatable="false">HUSH</string> <string name="symbol" translatable="false">HUSH</string>
<string name="play_store_url" translatable="false">https://hush.is</string> <string name="telegram_url" translatable="false">https://t.me/hush_main</string>
<string name="website_url" translatable="false">https://hush.is</string>
</resources> </resources>

View File

@@ -6,9 +6,9 @@ object Deps {
const val kotlinVersion = "1.7.20" const val kotlinVersion = "1.7.20"
const val navigationVersion = "2.5.2" const val navigationVersion = "2.5.2"
const val compileSdkVersion = 31 const val compileSdkVersion = 33
const val minSdkVersion = 21 const val minSdkVersion = 21
const val targetSdkVersion = 30 const val targetSdkVersion = 33
const val versionName = "1.0.0" const val versionName = "1.0.0"
const val versionCode = 1_00_00 // last digits are alpha(0XX) beta(2XX) rc(4XX) release(8XX). Ex: 1_08_04_401 is an release candidate build of version 1.8.4 and 1_08_04_800 would be the final release. const val versionCode = 1_00_00 // last digits are alpha(0XX) beta(2XX) rc(4XX) release(8XX). Ex: 1_08_04_401 is an release candidate build of version 1.8.4 and 1_08_04_800 would be the final release.
const val packageName = "hush.android" const val packageName = "hush.android"
@@ -25,15 +25,7 @@ object Deps {
const val MULTIDEX = "androidx.multidex:multidex:2.0.1" const val MULTIDEX = "androidx.multidex:multidex:2.0.1"
const val PAGING = "androidx.paging:paging-runtime-ktx:2.1.2" const val PAGING = "androidx.paging:paging-runtime-ktx:2.1.2"
const val RECYCLER = "androidx.recyclerview:recyclerview:1.2.1" const val RECYCLER = "androidx.recyclerview:recyclerview:1.2.1"
object CameraX : Version("1.1.0-alpha05") {
val CAMERA2 = "androidx.camera:camera-camera2:$version"
val CORE = "androidx.camera:camera-core:$version"
val LIFECYCLE = "androidx.camera:camera-lifecycle:$version"
object View : Version("1.0.0-alpha27") {
val EXT = "androidx.camera:camera-extensions:$version"
val VIEW = "androidx.camera:camera-view:$version"
}
}
object Lifecycle : Version("2.4.0-alpha02") { object Lifecycle : Version("2.4.0-alpha02") {
val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version" val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
} }