Merge remote-tracking branch 'fekt/main' into main
This commit is contained in:
@@ -143,11 +143,16 @@ dependencies {
|
||||
implementation Deps.AndroidX.LEGACY
|
||||
implementation Deps.AndroidX.PAGING
|
||||
implementation Deps.AndroidX.RECYCLER
|
||||
implementation Deps.AndroidX.CameraX.CAMERA2
|
||||
implementation Deps.AndroidX.CameraX.CORE
|
||||
implementation Deps.AndroidX.CameraX.LIFECYCLE
|
||||
implementation Deps.AndroidX.CameraX.View.EXT
|
||||
implementation Deps.AndroidX.CameraX.View.VIEW
|
||||
|
||||
def camerax_version = "1.2.0-rc01"
|
||||
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||
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.Navigation.FRAGMENT_KTX
|
||||
implementation Deps.AndroidX.Navigation.UI_KTX
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest 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.CAMERA" />
|
||||
@@ -11,8 +11,12 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/ZcashTheme">
|
||||
<activity android:name=".ui.MainActivity" android:screenOrientation="portrait">
|
||||
android:theme="@style/ZcashTheme"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
@@ -55,6 +55,38 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||
tapped(PROFILE_RESCAN)
|
||||
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
|
||||
|
||||
/*
|
||||
@@ -67,33 +99,42 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||
onViewDevLogs()
|
||||
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() {
|
||||
getString(R.string.play_store_url).takeUnless { it.isBlank() }?.let { url ->
|
||||
private fun openGiteaLink() {
|
||||
getString(R.string.gitea_url).takeUnless { it.isBlank() }?.let { url ->
|
||||
mainActivity?.onLaunchUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEnterAwesomeMode() {
|
||||
(context as? MainActivity)?.safeNavigate(R.id.action_nav_profile_to_nav_awesome)
|
||||
?: throw IllegalStateException(
|
||||
"Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context?.javaClass?.simpleName}"
|
||||
)
|
||||
private fun openMastodonLink() {
|
||||
getString(R.string.mastodon_url).takeUnless { it.isBlank() }?.let { url ->
|
||||
mainActivity?.onLaunchUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -1,216 +1,341 @@
|
||||
package cash.z.ecc.android.ui.scan
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.camera.core.*
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.activityViewModels
|
||||
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.databinding.FragmentScanBinding
|
||||
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.send.SendViewModel
|
||||
import cash.z.ecc.android.util.twig
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
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 sendViewModel: SendViewModel by activityViewModels()
|
||||
private lateinit var windowManager: WindowInfoTracker
|
||||
|
||||
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
|
||||
|
||||
private var cameraExecutor: ExecutorService? = null
|
||||
/** Blocking camera operations are performed using this executor */
|
||||
private lateinit var cameraExecutor: ExecutorService
|
||||
|
||||
override fun inflate(inflater: LayoutInflater): FragmentScanBinding =
|
||||
FragmentScanBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (cameraExecutor != null) cameraExecutor?.shutdown()
|
||||
|
||||
// Initialize our background executor
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (!allPermissionsGranted()) getRuntimePermissions()
|
||||
}
|
||||
// Wait for the views to be properly laid out
|
||||
fragmentCameraBinding.viewFinder.post {
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
||||
cameraProviderFuture.addListener(
|
||||
Runnable {
|
||||
bindPreview(cameraProviderFuture.get())
|
||||
},
|
||||
ContextCompat.getMainExecutor(context)
|
||||
)
|
||||
// Keep track of the display in which this view is attached
|
||||
displayId = fragmentCameraBinding.viewFinder.display.displayId
|
||||
|
||||
// Set up the camera and its use cases
|
||||
setUpCamera()
|
||||
}
|
||||
|
||||
// Initialize back button
|
||||
_fragmentCameraBinding?.backButtonHitArea?.onClickNavBack()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_fragmentCameraBinding = null
|
||||
super.onDestroyView()
|
||||
cameraExecutor?.shutdown()
|
||||
cameraExecutor = null
|
||||
|
||||
// Shut down our background executor
|
||||
cameraExecutor.shutdown()
|
||||
}
|
||||
|
||||
private fun bindPreview(cameraProvider: ProcessCameraProvider) {
|
||||
// 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
|
||||
// it's worth keeping tabs on that implementation because they keep making breaking changes to these APIs!
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
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
|
||||
val metrics = DisplayMetrics().also { binding.preview.display.getRealMetrics(it) }
|
||||
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
|
||||
val rotation = binding.preview.display.rotation
|
||||
/*
|
||||
val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(requireActivity()).bounds
|
||||
Log.d("SilentDragon", "Screen metrics: ${metrics.width()} x ${metrics.height()}")
|
||||
val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())
|
||||
*/
|
||||
|
||||
val preview =
|
||||
Preview.Builder().setTargetName("Preview").setTargetAspectRatio(screenAspectRatio)
|
||||
.setTargetRotation(rotation).build()
|
||||
// Hardcode to square for now otherwise scanning doesn't work
|
||||
val screenAspectRatio = aspectRatio(1, 1)
|
||||
Log.d("SilentDragon", "Preview aspect ratio: $screenAspectRatio")
|
||||
|
||||
val cameraSelector = CameraSelector.Builder()
|
||||
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
||||
val rotation = fragmentCameraBinding.viewFinder.display.rotation
|
||||
|
||||
// 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()
|
||||
|
||||
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)
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
|
||||
imageAnalysis.setAnalyzer(
|
||||
cameraExecutor!!,
|
||||
QrAnalyzer { q, i ->
|
||||
onQrScanned(q, i)
|
||||
|
||||
// The analyzer can then be assigned to the instance
|
||||
.also {
|
||||
it.setAnalyzer(cameraExecutor,
|
||||
QrAnalyzer { q, i ->
|
||||
onQrScanned(q, i)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Must unbind the use-cases before rebinding them
|
||||
cameraProvider.unbindAll()
|
||||
|
||||
if (camera != null) {
|
||||
// Must remove observers from the previous camera instance
|
||||
removeCameraStateObservers(camera!!.cameraInfo)
|
||||
}
|
||||
|
||||
try {
|
||||
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
|
||||
preview.setSurfaceProvider(binding.preview.surfaceProvider)
|
||||
} catch (t: Throwable) {
|
||||
// TODO: consider bubbling this up to the user
|
||||
mainActivity?.feedback?.report(t)
|
||||
twig("Error while opening the camera: $t")
|
||||
// A variable number of use-cases can be passed here -
|
||||
// camera provides access to CameraControl & CameraInfo
|
||||
camera = cameraProvider.bindToLifecycle(
|
||||
this, cameraSelector, preview, imageCapture, imageAnalyzer)
|
||||
|
||||
// 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 {
|
||||
val previewRatio = kotlin.math.max(width, height).toDouble() / kotlin.math.min(
|
||||
width,
|
||||
height
|
||||
)
|
||||
if (kotlin.math.abs(previewRatio - (4.0 / 3.0))
|
||||
<= kotlin.math.abs(previewRatio - (16.0 / 9.0))
|
||||
) {
|
||||
val previewRatio = max(width, height).toDouble() / min(width, height)
|
||||
if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
|
||||
return AspectRatio.RATIO_4_3
|
||||
}
|
||||
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) {
|
||||
//Log.d("SilentDragon", "QR scanned: $qrContent")
|
||||
resumedScope.launch {
|
||||
val parsed = viewModel.parse(qrContent)
|
||||
if (parsed == null) {
|
||||
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()
|
||||
} else { /* continue scanning*/
|
||||
binding.textScanError.text = ""
|
||||
_fragmentCameraBinding?.textScanError?.text = ""
|
||||
sendViewModel.toAddress = parsed
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ class ScanViewModel : ViewModel() {
|
||||
val networkName get() = synchronizer.network.networkName
|
||||
|
||||
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
|
||||
val address = if (qrCode.startsWith("zcash:")) {
|
||||
val address = if (qrCode.startsWith("hush:")) {
|
||||
qrCode.substring(6, qrCode.indexOf("?").takeUnless { it == -1 } ?: qrCode.length)
|
||||
} else {
|
||||
qrCode
|
||||
|
||||
@@ -63,6 +63,9 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
|
||||
binding.buttonPositive.setOnClickListener {
|
||||
onEnterWallet().also { if (hasBackUp) tapped(BACKUP_DONE) else tapped(BACKUP_VERIFY) }
|
||||
}
|
||||
binding.buttonSeedphraseMoreInfo.setOnClickListener {
|
||||
openSeedPhraseMoreInfoLink()
|
||||
}
|
||||
if (hasBackUp) {
|
||||
binding.buttonPositive.text = getString(R.string.backup_button_done)
|
||||
}
|
||||
@@ -155,4 +158,10 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private fun openSeedPhraseMoreInfoLink() {
|
||||
getString(R.string.seedphrase_more_info_url).takeUnless { it.isBlank() }?.let { url ->
|
||||
mainActivity?.onLaunchUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/ic_mastodon.xml
Normal file
9
app/src/main/res/drawable/ic_mastodon.xml
Normal 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>
|
||||
17
app/src/main/res/drawable/ic_matrix.xml
Normal file
17
app/src/main/res/drawable/ic_matrix.xml
Normal 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>
|
||||
9
app/src/main/res/drawable/ic_peertube.xml
Normal file
9
app/src/main/res/drawable/ic_peertube.xml
Normal 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>
|
||||
9
app/src/main/res/drawable/ic_telegram.xml
Normal file
9
app/src/main/res/drawable/ic_telegram.xml
Normal 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>
|
||||
9
app/src/main/res/drawable/ic_website.xml
Normal file
9
app/src/main/res/drawable/ic_website.xml
Normal 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>
|
||||
@@ -12,7 +12,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.667" />
|
||||
app:layout_constraintGuide_percent="0.75" />
|
||||
|
||||
|
||||
<!-- 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 -->
|
||||
<TextView
|
||||
android:id="@+id/text_message"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:gravity="center"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingEnd="32dp"
|
||||
android:text="@string/backup_instruction_store_words"
|
||||
android:textColor="@color/zcashWhite"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline_buttons"
|
||||
app:layout_constraintEnd_toEndOf="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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="320dp"
|
||||
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"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -86,16 +86,16 @@
|
||||
android:id="@+id/icon_profile"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/content_description_profile_zebra"
|
||||
android:elevation="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="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:srcCompat="@drawable/ic_profile_zebra_01"
|
||||
android:contentDescription="@string/content_description_profile_zebra" />
|
||||
app:srcCompat="@drawable/ic_profile_zebra_01" />
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_settings"
|
||||
@@ -148,45 +148,28 @@
|
||||
android:id="@+id/hit_area_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
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_constraintStart_toStartOf="@id/text_address"
|
||||
app:layout_constraintTop_toTopOf="@id/text_username"
|
||||
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
|
||||
android:id="@+id/button_backup"
|
||||
style="@style/Zcash.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.Button.OutlinedButton"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
android:text="@string/profile_backup_wallet"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
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
|
||||
android:id="@+id/button_rescan"
|
||||
@@ -210,7 +193,7 @@
|
||||
android:id="@+id/text_banner_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="64dp"
|
||||
android:layout_marginBottom="128dp"
|
||||
android:background="@drawable/background_banner"
|
||||
android:elevation="6dp"
|
||||
android:paddingStart="16dp"
|
||||
@@ -219,10 +202,10 @@
|
||||
android:text="@string/profile_ecc_wallet"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
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_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
@@ -237,4 +220,73 @@
|
||||
app:layout_constraintEnd_toEndOf="@id/text_banner_message"
|
||||
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>
|
||||
@@ -46,7 +46,7 @@
|
||||
tools:background="@color/zcashRed" />
|
||||
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/preview"
|
||||
android:id="@+id/viewFinder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
||||
@@ -282,6 +282,10 @@
|
||||
android:id="@+id/nav_backup"
|
||||
android:name="cash.z.ecc.android.ui.setup.BackupFragment"
|
||||
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
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
|
||||
<!-- 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_expecting">Expecting</string>
|
||||
<string name="custom_translation_syncing">Downloading…%1$d%%</string>
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<!-- ======================================================================================= -->
|
||||
|
||||
<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_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 -->
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<!-- Screen: Backup -->
|
||||
<string name="backup_button_done">@string/translated_button_done</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_instruction_store_words">@string/missing_backup_instruction_store_words</string>
|
||||
<string name="backup_slogan">@string/missing_backup_slogan</string>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<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="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>
|
||||
|
||||
@@ -6,9 +6,9 @@ object Deps {
|
||||
const val kotlinVersion = "1.7.20"
|
||||
const val navigationVersion = "2.5.2"
|
||||
|
||||
const val compileSdkVersion = 31
|
||||
const val compileSdkVersion = 33
|
||||
const val minSdkVersion = 21
|
||||
const val targetSdkVersion = 30
|
||||
const val targetSdkVersion = 33
|
||||
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 packageName = "hush.android"
|
||||
@@ -25,15 +25,7 @@ object Deps {
|
||||
const val MULTIDEX = "androidx.multidex:multidex:2.0.1"
|
||||
const val PAGING = "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||
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") {
|
||||
val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user