Initial commit.

This commit is contained in:
Kevin Gorham 2019-11-26 15:29:16 -05:00
parent c2e395a4b7
commit 343b7c4cf3
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
79 changed files with 2477 additions and 9 deletions

20
.gitignore vendored
View File

@ -2,6 +2,10 @@
*.apk
*.ap_
# Built libraries
#*.aar
# *.so
# Files for the ART/Dalvik VM
*.dex
@ -12,6 +16,7 @@
bin/
gen/
out/
target/
# Gradle files
.gradle/
@ -19,6 +24,7 @@ build/
# Local configuration file (sdk path, etc)
local.properties
.DS_Store
# Proguard folder generated by Eclipse
proguard/
@ -34,17 +40,10 @@ captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
.idea/caches
.idea
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
@ -63,3 +62,6 @@ fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# rust
.cargo/

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

104
app/build.gradle Normal file
View File

@ -0,0 +1,104 @@
import cash.z.ecc.android.Deps
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
//apply plugin: 'com.github.ben-manes.versions'
archivesBaseName = 'zcash-android-wallet'
group = 'cash.z.ecc.android'
version = '1.0.0-alpha01'
android {
compileSdkVersion Deps.compileSdkVersion
buildToolsVersion Deps.buildToolsVersion
viewBinding.enabled = true
defaultConfig {
applicationId 'cash.z.ecc.android'
minSdkVersion Deps.minSdkVersion
targetSdkVersion Deps.targetSdkVersion
versionCode = 1_00_00_001
// 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.
versionName = "$version"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
flavorDimensions 'network'
productFlavors {
// would rather name them "testnet" and "mainnet" but product flavor names cannot start with the word "test"
zcashtestnet {
dimension 'network'
applicationId 'cash.z.ecc.android.testnet'
matchingFallbacks = ['zcashtestnet', 'debug']
}
zcashmainnet {
dimension 'network'
matchingFallbacks = ['zcashmainnet', 'release']
}
}
buildTypes {
release {
minifyEnabled true
useProguard false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
useProguard false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
// mock {
// initWith debug
// matchingFallbacks = ['debug', 'release', 'zcashtestnet']
// }
}
signingConfigs {
debug {
// storeFile file("debug.keystore")
// keyAlias "androiddebugkey"
// keyPassword "android"
// storePassword "android"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':qrecycler')
// Kotlin
implementation Deps.Kotlin.STDLIB
// Android
implementation Deps.AndroidX.APPCOMPAT
implementation Deps.AndroidX.CORE_KTX
implementation Deps.AndroidX.CONSTRAINT_LAYOUT
implementation Deps.AndroidX.Lifecycle.LIFECYCLE_RUNTIME_KTX
implementation Deps.AndroidX.Navigation.FRAGMENT_KTX
implementation Deps.AndroidX.Navigation.UI_KTX
// Google
implementation Deps.Google.MATERIAL
// Dagger
implementation Deps.Dagger.ANDROID_SUPPORT
kapt Deps.Dagger.ANDROID_PROCESSOR
kapt Deps.Dagger.COMPILER
// Tests
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation Deps.Test.JUNIT
androidTestImplementation Deps.Test.Android.JUNIT
androidTestImplementation Deps.Test.Android.ESPRESSO
}

23
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,23 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-dontobfuscate
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package cash.z.ecc.android
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("cash.z.ecc.android", appContext.packageName)
}
}

View File

@ -0,0 +1,20 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cash.z.ecc.android">
<application
android:name="cash.z.ecc.android.ZcashWalletApp"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/ZcashTheme">
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,43 @@
package cash.z.ecc.android
import android.content.Context
import cash.z.ecc.android.di.DaggerAppComponent
import dagger.android.AndroidInjector
import dagger.android.DaggerApplication
class ZcashWalletApp : DaggerApplication() {
override fun onCreate() {
instance = this
// Setup handler for uncaught exceptions.
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
// Twig.plant(TroubleshootingTwig())
}
/**
* Implement the HasActivityInjector behavior so that dagger knows which [AndroidInjector] to use.
*/
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
// MultiDex.install(this)
}
companion object {
lateinit var instance: ZcashWalletApp
}
class ExceptionReporter(val ogHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler {
override fun uncaughtException(t: Thread?, e: Throwable?) {
// trackCrash(e, "Top-level exception wasn't caught by anything else!")
// Analytics.clear()
ogHandler.uncaughtException(t, e)
}
}
}

View File

@ -0,0 +1,32 @@
package cash.z.ecc.android.di
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.ui.MainActivityModule
import cash.z.ecc.android.ui.detail.WalletDetailFragmentModule
import cash.z.ecc.android.ui.home.HomeFragmentModule
import cash.z.ecc.android.ui.receive.ReceiveFragmentModule
import cash.z.ecc.android.ui.send.SendFragmentModule
import dagger.Component
import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton
@Component(
modules = [
AndroidSupportInjectionModule::class,
// Activities
MainActivityModule::class,
// Fragments
HomeFragmentModule::class,
ReceiveFragmentModule::class,
SendFragmentModule::class,
WalletDetailFragmentModule::class
]
)
interface AppComponent : AndroidInjector<ZcashWalletApp> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<ZcashWalletApp>()
}

View File

@ -0,0 +1,7 @@
package cash.z.ecc.android.di.annotation
import javax.inject.Scope
@Scope
@Retention(AnnotationRetention.SOURCE)
annotation class ActivityScope

View File

@ -0,0 +1,7 @@
package cash.z.ecc.android.di.annotation
import javax.inject.Scope
@Scope
@Retention(AnnotationRetention.SOURCE)
annotation class ApplicationScope

View File

@ -0,0 +1,7 @@
package cash.z.ecc.android.di.annotation
import javax.inject.Scope
@Scope
@Retention(AnnotationRetention.SOURCE)
annotation class FragmentScope

View File

@ -0,0 +1,93 @@
package cash.z.ecc.android.ui
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.content.getSystemService
import androidx.navigation.NavController
import androidx.navigation.findNavController
import cash.z.ecc.android.R
import cash.z.ecc.android.di.annotation.ActivityScope
import com.google.android.material.snackbar.Snackbar
import dagger.Module
import dagger.android.ContributesAndroidInjector
import dagger.android.support.DaggerAppCompatActivity
class MainActivity : DaggerAppCompatActivity() {
lateinit var navController: NavController
private var snackbar: Snackbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
initNavigation()
window.statusBarColor = Color.TRANSPARENT;
window.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
setWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false)// | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, false)
}
private fun setWindowFlag(bits: Int, on: Boolean) {
val win = window
val winParams = win.attributes
if (on) {
winParams.flags = winParams.flags or bits
} else {
winParams.flags = winParams.flags and bits.inv()
}
win.attributes = winParams
}
private fun initNavigation() {
navController = findNavController(R.id.nav_host_fragment)
navController.addOnDestinationChangedListener { _, _, _ ->
// hide the keyboard anytime we change destinations
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(
this@MainActivity.window.decorView.rootView.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
fun copyAddress(view: View) {
// TODO: get address from synchronizer
val address = "zs1qduvdyuv83pyygjvc4cfcuc2wj5flnqn730iigf0tjct8k5ccs9y30p96j2gvn9gzyxm6q0vj12c4"
val clipboard: ClipboardManager =
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(
ClipData.newPlainText(
"Z-Address",
address
)
)
showMessage("Address copied!", "Sweet")
}
private fun showMessage(message: String, action: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// val view = findViewById<View>(android.R.id.content)
// if (snackbar == null) {
// snackbar = Snackbar.make(view, "$message", Snackbar.LENGTH_INDEFINITE)
// .setAction(action) { /*auto-close*/ }
// } else {
// snackbar?.setText(message)
// }
// if (snackbar?.isShownOrQueued == false) snackbar?.show()
}
}
@Module
abstract class MainActivityModule {
@ActivityScope
@ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
}

View File

@ -0,0 +1,29 @@
package cash.z.ecc.android.ui.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.NonNull
import androidx.viewbinding.ViewBinding
import cash.z.ecc.android.ui.MainActivity
import dagger.android.support.DaggerFragment
abstract class BaseFragment<T : ViewBinding> : DaggerFragment() {
val mainActivity: MainActivity? get() = activity as MainActivity?
lateinit var binding: T
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = inflate(inflater)
return binding.root
}
// inflate is static in the ViewBinding class so we can't handle this ourselves
// each fragment must call FragmentMyLayoutBinding.inflate(inflater)
abstract fun inflate(@NonNull inflater: LayoutInflater): T
}

View File

@ -0,0 +1,21 @@
package cash.z.ecc.android.ui.detail
import android.view.LayoutInflater
import cash.z.ecc.android.databinding.FragmentDetailBinding
import cash.z.ecc.android.di.annotation.FragmentScope
import cash.z.ecc.android.ui.base.BaseFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
class WalletDetailFragment : BaseFragment<FragmentDetailBinding>() {
override fun inflate(inflater: LayoutInflater): FragmentDetailBinding =
FragmentDetailBinding.inflate(inflater)
}
@Module
abstract class WalletDetailFragmentModule {
@FragmentScope
@ContributesAndroidInjector
abstract fun contributeWalletDetailFragment(): WalletDetailFragment
}

View File

@ -0,0 +1,37 @@
package cash.z.ecc.android.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentHomeBinding
import cash.z.ecc.android.di.annotation.FragmentScope
import cash.z.ecc.android.ui.base.BaseFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
class HomeFragment : BaseFragment<FragmentHomeBinding>() {
override fun inflate(inflater: LayoutInflater): FragmentHomeBinding =
FragmentHomeBinding.inflate(inflater)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.hitAreaReceive.setOnClickListener {
mainActivity?.navController?.navigate(R.id.action_nav_home_to_nav_receive)
}
binding.iconDetail.setOnClickListener {
mainActivity?.navController?.navigate(R.id.action_nav_home_to_nav_detail)
}
binding.hitAreaScan.setOnClickListener {
mainActivity?.navController?.navigate(R.id.action_nav_home_to_nav_send)
}
}
}
@Module
abstract class HomeFragmentModule {
@FragmentScope
@ContributesAndroidInjector
abstract fun contributeHomeFragment(): HomeFragment
}

View File

@ -0,0 +1,85 @@
package cash.z.ecc.android.ui.receive
import android.content.Context
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import cash.z.android.qrecycler.QRecycler
import cash.z.ecc.android.databinding.FragmentReceiveBinding
import cash.z.ecc.android.di.annotation.FragmentScope
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.util.AddressPartNumberSpan
import dagger.Module
import dagger.android.ContributesAndroidInjector
import kotlinx.android.synthetic.main.fragment_receive.*
import kotlinx.coroutines.launch
class ReceiveFragment : BaseFragment<FragmentReceiveBinding>() {
override fun inflate(inflater: LayoutInflater): FragmentReceiveBinding =
FragmentReceiveBinding.inflate(inflater)
lateinit var qrecycler: QRecycler
lateinit var addressParts: Array<TextView>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addressParts = arrayOf(
text_address_part_1,
text_address_part_2,
text_address_part_3,
text_address_part_4,
text_address_part_5,
text_address_part_6,
text_address_part_7,
text_address_part_8
)
}
override fun onAttach(context: Context) {
qrecycler = QRecycler() // inject! :)
super.onAttach(context)
}
override fun onResume() {
super.onResume()
lifecycleScope.launch {
onAddressLoaded("zs1qduvdyuv83pyygjvc4cfcuc2wj5flnqn730iigf0tjct8k5ccs9y30p96j2gvn9gzyxm6q0vj12c4")
}
}
private fun onAddressLoaded(address: String) {
Log.e("TWIG", "onAddressLoaded: $address length: ${address.length}")
qrecycler.load(address)
.withQuietZoneSize(3)
.withCorrectionLevel(QRecycler.CorrectionLevel.MEDIUM)
.into(receive_qr_code)
address.chunked(address.length/8).forEachIndexed { i, part ->
setAddressPart(i, part)
}
}
private fun setAddressPart(index: Int, addressPart: String) {
Log.e("TWIG", "setting address for part $index) $addressPart")
val thinSpace = "\u2005" // 0.25 em space
val textSpan = SpannableString("${index + 1}$thinSpace$addressPart")
textSpan.setSpan(AddressPartNumberSpan(), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
addressParts[index].text = textSpan
}
}
@Module
abstract class ReceiveFragmentModule {
@FragmentScope
@ContributesAndroidInjector
abstract fun contributeReceiveFragment(): ReceiveFragment
}

View File

@ -0,0 +1,21 @@
package cash.z.ecc.android.ui.send
import android.view.LayoutInflater
import cash.z.ecc.android.databinding.FragmentSendBinding
import cash.z.ecc.android.di.annotation.FragmentScope
import cash.z.ecc.android.ui.base.BaseFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
class SendFragment : BaseFragment<FragmentSendBinding>() {
override fun inflate(inflater: LayoutInflater): FragmentSendBinding =
FragmentSendBinding.inflate(inflater)
}
@Module
abstract class SendFragmentModule {
@FragmentScope
@ContributesAndroidInjector
abstract fun contributeSendFragment(): SendFragment
}

View File

@ -0,0 +1,28 @@
package cash.z.ecc.android.ui.util
import android.text.TextPaint
import android.text.style.MetricAffectingSpan
import androidx.core.content.ContextCompat
import cash.z.ecc.android.R
import cash.z.ecc.android.ZcashWalletApp
/**
* A span used for numbering the parts of an address. It combines a [android.text.style.RelativeSizeSpan],
* [android.text.style.SuperscriptSpan], and a [android.text.style.ForegroundColorSpan] into one class for efficiency.
*/
class AddressPartNumberSpan(
val proportion: Float = 0.5f,
val color: Int = ContextCompat.getColor(ZcashWalletApp.instance, R.color.colorPrimary)
) : MetricAffectingSpan() {
override fun updateMeasureState(textPaint: TextPaint) {
textPaint.baselineShift += (textPaint.ascent() / 2).toInt() // from SuperscriptSpan
textPaint.textSize = textPaint.textSize * proportion // from RelativeSizeSpan
}
override fun updateDrawState(textPaint: TextPaint) {
textPaint.baselineShift += (textPaint.ascent() / 2).toInt() // from SuperscriptSpan (baseline must shift before resizing or else it will not properly align to the top of the text)
textPaint.textSize = textPaint.textSize * proportion // from RelativeSizeSpan
textPaint.color = color // from ForegroundColorSpan
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:color="@color/text_light"/>
<item android:state_pressed="true" android:color="@color/text_dark" />
</selector>

View File

@ -0,0 +1,31 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
android:width="108dp">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="34dp" />
<gradient
android:startColor="@color/zcashYellow_dark"
android:endColor="@color/zcashYellow"
android:angle="180"
android:type="linear" />
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="48dp"
android:height="48dp"/>
<solid
android:color="@color/zcashWhite_60" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:endColor="#000000"
android:startColor="#28282C"
android:type="radial"
android:centerY="0.36"
android:centerX="0.50"
android:gradientRadius="640dp"/>
</shape>

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
android:width="108dp">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/black"
android:pathData="M3,11H5V13H3V11M11,5H13V9H11V5M9,11H13V15H11V13H9V11M15,11H17V13H19V11H21V13H19V15H21V19H19V21H17V19H13V21H11V17H15V15H17V13H15V11M19,19V15H17V19H19M15,3H21V9H15V3M17,5V7H19V5H17M3,3H9V9H3V3M5,5V7H7V5H5M3,15H9V21H3V15M5,17V19H7V17H5Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/black"
android:pathData="M18,17L6,17v-2h12v2zM18,13L6,13v-2h12v2zM18,9L6,9L6,7h12v2zM3,22l1.5,-1.5L6,22l1.5,-1.5L9,22l1.5,-1.5L12,22l1.5,-1.5L15,22l1.5,-1.5L18,22l1.5,-1.5L21,22L21,2l-1.5,1.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2 7.5,3.5 6,2 4.5,3.5 3,2v20z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="23dp"
android:height="28dp"
android:viewportWidth="23"
android:viewportHeight="28">
<path
android:pathData="M23,4.5167L23,15.5777C23,19.5236 20.7958,23.1307 17.25,24.9876L11.5,28L5.75,24.9876C2.2042,23.1307 0,19.5236 0,15.5777L0,4.5167L11.5,0L23,4.5167ZM11.2157,14.2963L16.2746,9L19,11.8519L11.2157,20L6,14.541L8.7254,11.6892L11.2157,14.2963Z"
android:strokeWidth="1"
android:fillColor="#FFB727"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="358dp"
android:height="418dp"
android:viewportWidth="358"
android:viewportHeight="418">
<path
android:pathData="M179,0C207.069,11.901 236.599,23.2 267.591,33.897C298.582,44.595 328.719,53.128 358,59.497C354.385,162.895 337.225,240.396 306.519,292C275.814,343.603 233.307,385.603 179,418C124.693,385.603 82.186,343.603 51.481,292C20.775,240.396 3.615,162.895 0,59.497C29.281,53.128 59.418,44.595 90.409,33.897C121.401,23.2 150.931,11.901 179,0Z"
android:strokeAlpha="0.2"
android:strokeWidth="1"
android:fillColor="#A1A1A1"
android:fillType="nonZero"
android:strokeColor="#00000000"
android:fillAlpha="1.0"/>
</vector>

View File

@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="451dp"
android:height="451dp"
android:viewportWidth="451"
android:viewportHeight="451">
<path
android:pathData="M225.4,0C101.11,0 0,101.11 0,225.39C0,349.67 101.11,450.79 225.4,450.79C349.69,450.79 450.79,349.68 450.79,225.39C450.79,101.1 349.68,0 225.4,0ZM225.4,401.79C128.131,401.79 49,322.659 49,225.39C49,128.122 128.131,49 225.4,49C322.668,49 401.79,128.131 401.79,225.39C401.79,322.65 322.659,401.79 225.4,401.79Z"
android:strokeWidth="1"
android:fillColor="#231F20"
android:fillType="nonZero"
android:strokeColor="#00000000"/>
<path
android:pathData="M225.4,225.39m-177.65,0a177.65,177.65 0,1 1,355.3 0a177.65,177.65 0,1 1,-355.3 0"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:strokeColor="#00000000"/>
<path
android:pathData="M305.78,155.13l0,-34.38l-61.5,0l0,-37.73l-37.77,0l0,37.73l-61.49,0l0,45.54l95.33,0l-77.96,107.29l-17.37,22.08l0,34.38l61.49,0l0,37.61l4.53,0l0,0.16l28.71,0l0,-0.16l4.53,0l0,-37.61l61.5,0l0,-45.54l-95.34,0l77.96,-107.29z"
android:strokeWidth="1"
android:fillColor="#231F20"
android:fillType="nonZero"
android:strokeColor="#00000000"/>
</vector>

View File

@ -0,0 +1,64 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="50dp"
android:height="50dp"
android:viewportWidth="50"
android:viewportHeight="50">
<path
android:pathData="M25,25m-22.75,0a22.75,22.75 0,1 1,45.5 0a22.75,22.75 0,1 1,-45.5 0"
android:strokeLineJoin="round"
android:strokeWidth="0.5"
android:fillColor="#FFFFFF"
android:fillType="evenOdd">
<aapt:attr name="android:strokeColor">
<gradient
android:startY="2.25"
android:startX="25"
android:endY="47.304756"
android:endX="25"
android:type="linear">
<item android:offset="0" android:color="#00000000"/>
<item android:offset="0.8" android:color="#05000000"/>
<item android:offset="1" android:color="#0A000000"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M25,25m-22.75,0a22.75,22.75 0,1 1,45.5 0a22.75,22.75 0,1 1,-45.5 0"
android:strokeLineJoin="round"
android:strokeWidth="0.5"
android:fillColor="#00000000"
android:fillType="evenOdd">
<aapt:attr name="android:strokeColor">
<gradient
android:startY="2.25"
android:startX="25"
android:endY="47.75"
android:endX="25"
android:type="linear">
<item android:offset="0" android:color="#1EFFFFFF"/>
<item android:offset="0.2" android:color="#0FFFFFFF"/>
<item android:offset="1" android:color="#00FFFFFF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M25.0005,2C12.3176,2 2,12.3176 2,24.9995C2,37.6814 12.3176,48 25.0005,48C37.6834,48 48,37.6824 48,24.9995C48,12.3166 37.6824,2 25.0005,2ZM25.0005,44.203C14.4115,44.203 5.797,35.5885 5.797,24.9995C5.797,14.4105 14.4115,5.797 25.0005,5.797C35.5895,5.797 44.203,14.4115 44.203,24.9995C44.203,35.5875 35.5885,44.203 25.0005,44.203Z"
android:strokeWidth="1"
android:fillColor="#231F20"
android:fillType="nonZero"
android:strokeColor="#00000000"/>
<path
android:pathData="M24.9747,24.9747m-18.0462,0a18.0462,18.0462 0,1 1,36.0923 0a18.0462,18.0462 0,1 1,-36.0923 0"
android:strokeWidth="1"
android:fillColor="#F4B728"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
<path
android:pathData="M33.0626,17.5611l0,-3.5028l-6.2269,0l0,-3.8441l-3.8242,0l0,3.8441l-6.2259,0l0,4.6398l9.6521,0l-7.8934,10.9311l-1.7587,2.2496l0,3.5028l6.2259,0l0,3.8318l0.4587,0l0,0.0163l2.9069,0l0,-0.0163l0.4587,0l0,-3.8318l6.2269,0l0,-4.6398l-9.6532,0l7.8934,-10.9311z"
android:strokeWidth="1"
android:fillColor="#231F20"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11dp"
android:height="19dp"
android:viewportWidth="11"
android:viewportHeight="19">
<path
android:pathData="M10.1895,4.8011l0,-2.2884l-3.8975,0l0,-2.5127l-2.3939,0l0,2.5127l-3.8981,0l0,3.0313l6.0424,0l-4.941,7.144l-1.1014,1.4698l0,2.289l3.8981,0l0,2.5044l0.2868,0l0,0.0103l1.8197,0l0,-0.0103l0.2874,0l0,-2.5044l3.8975,0l0,-3.0313l-6.0424,0l4.941,-7.1447z"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@android:color/white">
<item android:drawable="@drawable/background_circle" />
</ripple>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/transparent" android:state_pressed="false" />
<item android:drawable="@drawable/ripple_button_circle" android:state_pressed="true" />
</selector>

Binary file not shown.

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_home"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_light"
android:text="WALLET DETAIL"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,332 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_home">
<ImageView
android:id="@+id/icon_scan"
android:layout_width="0dp"
android:layout_height="0dp"
android:tint="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.108"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0574"
app:layout_constraintHeight_percent="0.052"
app:layout_constraintWidth_percent="0.0887"
app:srcCompat="@drawable/ic_qrcode_24dp" />
<ImageView
android:id="@+id/icon_receive"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.8883"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.06420765027"
app:layout_constraintHeight_percent="0.03825136612"
app:layout_constraintWidth_percent="0.05582524272"
app:srcCompat="@drawable/ic_receive_funds" />
<ImageView
android:id="@+id/icon_detail"
android:layout_width="0dp"
android:layout_height="0dp"
android:tint="@color/colorAccent"
app:layout_constraintTop_toTopOf="@id/text_detail"
app:layout_constraintBottom_toBottomOf="@id/text_detail"
app:layout_constraintEnd_toStartOf="@id/text_detail"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHeight_percent="0.044"
app:layout_constraintWidth_percent="0.0887"
app:srcCompat="@drawable/ic_receipt_24dp"
app:layout_constraintHorizontal_chainStyle="packed"/>
<TextView
android:id="@+id/text_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:tint="@color/colorAccent"
android:text="Wallet Details"
android:textColor="@color/colorAccent"
app:layout_constraintTop_toBottomOf="@id/button_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="@dimen/ratio_golden_small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon_detail"/>
<TextView
android:id="@+id/text_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:includeFontPadding="false"
android:text="12.345"
android:textSize="72dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/text_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/guide_keys"/>
<TextView
android:id="@+id/text_balance_available"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12.34581242 ZEC"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/text_light"
app:layout_constraintTop_toBottomOf="@id/text_amount"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/label_available"
app:layout_constraintHorizontal_chainStyle="packed"/>
<TextView
android:id="@+id/label_available"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:text="Available"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/colorPrimary"
app:layout_constraintBaseline_toBaselineOf="@id/text_balance_available"
app:layout_constraintStart_toEndOf="@id/text_balance_available"
app:layout_constraintEnd_toEndOf="parent"/>
<ImageView
android:id="@+id/icon_zec_symbol"
android:layout_width="0dp"
android:layout_height="0dp"
android:tint="@color/colorAccent"
app:layout_constraintWidth_percent="0.060"
app:layout_constraintHeight_percent="0.052"
app:layout_constraintTop_toTopOf="@id/text_amount"
app:layout_constraintBottom_toBottomOf="@id/text_amount"
app:layout_constraintEnd_toStartOf="@id/text_amount"
app:layout_constraintVertical_bias="0.2"
app:srcCompat="@drawable/ic_zec_symbol" />
<View
android:id="@+id/guide_keys"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.3"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.38196601125"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.58"
app:layout_constraintWidth_percent="0.7475728155" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_1"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="1"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintTop_toTopOf="@id/guide_keys"
app:layout_constraintBottom_toTopOf="@id/button_calculator_4"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintVertical_chainStyle="spread_inside"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_2"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="2"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toTopOf="@id/guide_keys"
app:layout_constraintBottom_toTopOf="@id/button_calculator_5"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintVertical_chainStyle="spread_inside"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_3"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="3"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toTopOf="@id/guide_keys"
app:layout_constraintBottom_toTopOf="@id/button_calculator_6"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintVertical_chainStyle="spread_inside"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_4"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="4"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_1"
app:layout_constraintBottom_toTopOf="@id/button_calculator_7"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_5"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="5"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_2"
app:layout_constraintBottom_toTopOf="@id/button_calculator_8"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_6"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="6"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_3"
app:layout_constraintBottom_toTopOf="@id/button_calculator_9"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_7"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="7"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_4"
app:layout_constraintBottom_toTopOf="@id/button_calculator_decimal"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_8"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="8"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_5"
app:layout_constraintBottom_toTopOf="@id/button_calculator_0"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_9"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="9"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_6"
app:layout_constraintBottom_toTopOf="@id/button_calculator_back"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_decimal"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="."
android:paddingBottom="2dp"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_7"
app:layout_constraintBottom_toBottomOf="@id/guide_keys"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_0"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="0"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_8"
app:layout_constraintBottom_toBottomOf="@id/guide_keys"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button_calculator_back"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="&lt;"
style="@style/Zcash.TextView.Calculator"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/button_calculator_9"
app:layout_constraintBottom_toBottomOf="@id/guide_keys"
app:layout_constraintWidth_percent="@dimen/calculator_button_width_percent" />
<!-- TODO: properly style this as a button with ripples -->
<TextView
android:id="@+id/button_send"
android:layout_width="0dp"
android:layout_height="46dp"
android:layout_marginTop="24dp"
android:background="@drawable/background_button_rounded"
android:gravity="center"
android:text="Send Amount"
android:textColor="#000000"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintEnd_toEndOf="@id/guide_keys"
app:layout_constraintStart_toStartOf="@id/guide_keys"
app:layout_constraintTop_toBottomOf="@id/guide_keys" />
<!-- Hit Areas -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_hit_area_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.04" />
<View
android:id="@+id/hit_area_scan"
android:layout_width="68dp"
android:layout_height="68dp"
android:layout_marginStart="24dp"
android:alpha="0.3"
android:background="@android:color/transparent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline_hit_area_top" />
<View
android:id="@+id/hit_area_receive"
android:layout_width="68dp"
android:layout_height="68dp"
android:layout_marginEnd="24dp"
android:background="@android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline_hit_area_top" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/icon_scan"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_qrcode_24dp" />
<ImageView
android:id="@+id/icon_receive"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_zcashlogo" />
<ImageView
android:id="@+id/icon_detail"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_receipt_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_receive_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_home"
android:onClick="copyAddress"
tools:context=".ui.fragment.ReceiveFragment">
<!-- <include-->
<!-- android:id="@+id/main_app_bar"-->
<!-- layout="@layout/include_main_app_bar"-->
<!-- android:visibility="invisible"-->
<!-- tools:ignore="MissingConstraints" />-->
<!-- Shield Background -->
<ImageView
android:id="@+id/image_shield"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_shield"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintDimensionRatio="H,1:1.1676"
app:layout_constraintVertical_bias="0.08"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.84988" />
<!-- QR code placeholder -->
<ImageView
android:id="@+id/receive_qr_code"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="@id/image_shield"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/image_shield"
app:layout_constraintVertical_bias="0.272"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.59" />
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
app:srcCompat="@drawable/ic_zcash_white"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.12"
app:layout_constraintStart_toStartOf="@id/receive_qr_code"
app:layout_constraintEnd_toEndOf="@id/receive_qr_code"
app:layout_constraintTop_toTopOf="@id/receive_qr_code"
app:layout_constraintBottom_toBottomOf="@id/receive_qr_code"
/>
<View
android:id="@+id/receive_address_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#c0171717"
app:layout_constraintBottom_toBottomOf="@id/receive_address_parts"
app:layout_constraintEnd_toEndOf="@id/image_shield"
app:layout_constraintStart_toStartOf="@id/image_shield"
app:layout_constraintTop_toTopOf="@id/receive_address_parts" />
<TextView
android:id="@+id/receive_title"
style="@style/Zcash.TextAppearance.Headline6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:background="#D8FFB727"
android:gravity="center"
android:padding="16dp"
android:text="@string/receive_address_title"
android:textColor="@color/zcashWhite"
app:layout_constraintEnd_toEndOf="@+id/image_shield"
app:layout_constraintStart_toStartOf="@+id/image_shield"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<!-- Address parts -->
<!-- Someday, there will be an advanced VirtualLayout that helps us do this without nesting but for now, this seems to be the only clean way to center all the fields -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/receive_address_parts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foregroundGravity="center"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="@id/receive_title"
app:layout_constraintStart_toStartOf="@id/receive_title"
app:layout_constraintTop_toBottomOf="@id/receive_title">
<TextView
android:id="@+id/text_address_part_1"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" ztestsaplin"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/text_address_part_3"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" jceuu9s2p6t"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_5"
app:layout_constraintStart_toStartOf="@id/text_address_part_1"
app:layout_constraintTop_toBottomOf="@id/text_address_part_1" />
<TextView
android:id="@+id/text_address_part_5"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" 7u7uarqls7d"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_7"
app:layout_constraintStart_toStartOf="@id/text_address_part_1"
app:layout_constraintTop_toBottomOf="@id/text_address_part_3" />
<TextView
android:id="@+id/text_address_part_7"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" rzq85xggu56"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/text_address_part_1"
app:layout_constraintTop_toBottomOf="@id/text_address_part_5" />
<TextView
android:id="@+id/text_address_part_2"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:text=" g1mwjzlg62j"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_4"
app:layout_constraintStart_toEndOf="@id/barrier_left_address_column"
app:layout_constraintTop_toTopOf="@id/text_address_part_1" />
<TextView
android:id="@+id/text_address_part_4"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" wns6qxwec6v"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_6"
app:layout_constraintStart_toStartOf="@id/text_address_part_2"
app:layout_constraintTop_toBottomOf="@id/text_address_part_2" />
<TextView
android:id="@+id/text_address_part_6"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" gtg3tpgqxjd"
app:layout_constraintBottom_toTopOf="@+id/text_address_part_8"
app:layout_constraintStart_toStartOf="@id/text_address_part_2"
app:layout_constraintTop_toBottomOf="@id/text_address_part_4" />
<TextView
android:id="@+id/text_address_part_8"
style="@style/Zcash.TextAppearance.AddressPart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text=" k904xderng6"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/text_address_part_2"
app:layout_constraintTop_toBottomOf="@id/text_address_part_6" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_left_address_column"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="150dp"
android:layout_marginRight="150dp"
android:padding="150dp"
app:barrierDirection="end"
app:constraint_referenced_ids="text_address_part_1,text_address_part_3,text_address_part_5,text_address_part_7" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_home"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_light"
android:text="SCAN"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</FrameLayout>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon
xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon
xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="cash.z.ecc.android.ui.home.HomeFragment"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_nav_home_to_nav_receive"
app:destination="@id/nav_receive" />
<action
android:id="@+id/action_nav_home_to_nav_send"
app:destination="@id/nav_send" />
<action
android:id="@+id/action_nav_home_to_nav_detail"
app:destination="@id/nav_detail" />
</fragment>
<fragment
android:id="@+id/nav_send"
android:name="cash.z.ecc.android.ui.send.SendFragment"
tools:layout="@layout/fragment_send" />
<fragment
android:id="@+id/nav_receive"
android:name="cash.z.ecc.android.ui.receive.ReceiveFragment"
tools:layout="@layout/fragment_receive" />
<fragment
android:id="@+id/nav_detail"
android:name="cash.z.ecc.android.ui.detail.WalletDetailFragment"
tools:layout="@layout/fragment_detail" />
</navigation>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- -->
<!-- palette: Android -->
<!-- -->
<!-- colors here should map to a material design value -->
<color name="colorPrimary">@color/zcashYellow</color>
<color name="colorPrimaryDark">#4D3805</color>
<color name="colorAccent">#A1A1A1</color>
<!-- -->
<!-- palette: Zcash -->
<!-- -->
<!-- colors here should have a hex value and a generic name, prefixed with zcash -->
<!-- This palette should be kept as small as possible
and should map directly to the design style guide -->
<color name="zcashWhite">#F5F5F5</color>
<color name="zcashWhite_12">#1FFFFFFF</color>
<color name="zcashWhite_40">#66FFFFFF</color>
<color name="zcashWhite_50">#80FFFFFF</color>
<color name="zcashWhite_60">#A3FFFFFF</color>
<color name="zcashWhite_87">#BFFFFFFF</color>
<color name="zcashWhite_light">#EDEDED</color>
<color name="zcashBlack_light">#2B2B2B</color>
<color name="zcashBlack_12">#1F000000</color>
<color name="zcashBlack_40">#66000000</color>
<color name="zcashBlack_54">#8A000000</color>
<color name="zcashBlack_87">#DD000000</color>
<color name="zcashBlack_0">#00000000</color>
<!-- yellows -->
<color name="zcashYellow_light">#FFD649</color>
<color name="zcashYellow">#FFB727</color>
<color name="zcashYellow_dark">#FFA918</color>
<!-- -->
<!-- named colors -->
<!-- -->
<!-- every color here should be a reference to a palette color
but have a more useful name for use in code -->
<!-- text -->
<color name="text_light">#FFFFFF</color>
<color name="text_light_dimmed">@color/zcashWhite_50</color>
<color name="text_dark">@color/zcashBlack_87</color>
<color name="text_dark_dimmed">@color/zcashBlack_54</color>
<color name="text_shadow">@color/zcashBlack_40</color>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Floats -->
<item name="ratio_golden_small" format="float" type="dimen">0.381966</item>
<item name="ratio_golden_large" format="float" type="dimen">0.618034</item>
<item name="calculator_button_width_percent" format="float" type="dimen">0.145631068</item>
</resources>

View File

@ -0,0 +1,4 @@
<resources>
<string name="app_name">Zcash Wallet</string>
<string name="receive_address_title">Your Zcash shielded address</string>
</resources>

View File

@ -0,0 +1,44 @@
<resources>
<!-- Base application theme -->
<style name="ZcashBaseTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="ZcashTheme" parent="ZcashBaseTheme"/>
<style name="ZcashTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<!-- Text Styles -->
<style name="Zcash.TextView.Calculator" parent="Widget.AppCompat.TextView">
<item name="android:textAppearance">@style/Zcash.TextAppearance.Calculator</item>
<item name="android:background">@drawable/selector_pressed_ripple_circle</item>
</style>
<!-- Text Appearances -->
<style name="Zcash.TextAppearance.Calculator" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textSize">30dp</item>
<item name="android:textColor">@color/selector_button_text</item>
</style>
<style name="Zcash.TextAppearance.AddressPart" parent="TextAppearance.AppCompat">
<item name="android:fontFamily">@font/inconsolata</item>
<item name="fontFamily">@font/inconsolata</item>
<item name="android:textSize">22sp</item>
<item name="android:textColor">@color/text_light</item>
</style>
<style name="Zcash.TextAppearance.Headline6" parent="TextAppearance.MaterialComponents.Headline6">
<item name="fontFamily">sans-serif</item>
<item name="android:fontFamily">sans-serif</item>
</style>
</resources>

View File

@ -0,0 +1,17 @@
package cash.z.ecc.android
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

23
build.gradle Normal file
View File

@ -0,0 +1,23 @@
import cash.z.ecc.android.Deps
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.0-beta04'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}

View File

@ -0,0 +1,84 @@
package cash.z.ecc.android
object Deps {
// For use in the top-level build.gradle which gives an error when provided
// `Deps.Kotlin.version` directly
const val kotlinVersion = "1.3.60"
const val compileSdkVersion = 29
const val buildToolsVersion = "29.0.2"
const val minSdkVersion = 21
const val targetSdkVersion = 29
object AndroidX {
const val APPCOMPAT = "androidx.appcompat:appcompat:1.1.0"
const val CORE_KTX = "androidx.core:core-ktx:1.1.0"
const val CONSTRAINT_LAYOUT = "androidx.constraintlayout:constraintlayout:1.1.3"
const val FRAGMENT_KTX = "androidx.fragment:fragment-ktx:1.1.0-beta01"
const val MULTIDEX = "androidx.multidex:multidex:2.0.1"
object Navigation : Version("2.1.0") {
val FRAGMENT_KTX = "androidx.navigation:navigation-fragment-ktx:$version"
val UI_KTX = "androidx.navigation:navigation-ui-ktx:$version"
}
object Lifecycle: Version("2.2.0-rc02") {
val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
}
}
object Dagger : Version("2.25.2") {
val ANDROID_SUPPORT = "com.google.dagger:dagger-android-support:$version"
val ANDROID_PROCESSOR = "com.google.dagger:dagger-android-processor:$version"
val COMPILER = "com.google.dagger:dagger-compiler:$version"
}
object Google {
const val MATERIAL = "com.google.android.material:material:1.1.0-beta01"
}
object Kotlin : Version(kotlinVersion) {
val STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version"
object Coroutines : Version("1.3.2") {
val ANDROID = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version"
val CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version"
}
}
object Test {
const val JUNIT = "junit:junit:4.12"
object Android {
const val JUNIT = "androidx.test.ext:junit:1.1.1"
const val ESPRESSO = "androidx.test.espresso:espresso-core:3.2.0"
}
}
}
open class Version(@JvmField val version: String) {
@JvmField val t = version
}
//zzz
//zzz "androidx.constraintlayout:constraintlayout:2.0.0-alpha3"
//zzz "androidx.core:core:1.1.0-alpha05"
//zzz "androidx.core:core-ktx:1.0.0"
//zzz "androidx.fragment:fragment:1.1.0-beta01"
//zzz "androidx.fragment:fragment-ktx:1.1.0-beta01"
//zzz "androidx.multidex:multidex:2.0.1"
//zzz "androidx.navigation:navigation-fragment-ktx:${versions.navigation}"
//zzz "androidx.navigation:navigation-ui-ktx:${versions.navigation}"
//zzz "androidx.test.espresso:espresso-core:3.1.0"
//zzz "androidx.test:runner:1.1.0"
//zzz "cash.z.android.wallet:zcash-android-testnet:1.9.1-alpha@aar"
//zzz "com.airbnb.android:lottie:3.0.0"
//zzz "com.facebook.stetho:stetho:1.5.1"
//zzz "com.google.android.material:material:1.1.0-alpha06"
//zzz "com.google.dagger:dagger-android-processor:${versions.dagger}"
//zzz "com.google.dagger:dagger-android-support:${versions.dagger}"
//zzz "com.google.dagger:dagger-compiler:${versions.dagger}"
//zzz "com.leinardi.android:speed-dial:2.0.0"
//zzz "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
//zzz "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}"
//zzz "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
//zzz "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"
//zzz "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
//zzz "org.junit.jupiter:junit-jupiter-api:${versions.junit5}"
//zzz "org.junit.jupiter:junit-jupiter-api:${versions.junit5}"
//zzz "org.junit.jupiter:junit-jupiter-engine:${versions.junit5}"
//zzz "org.mockito:mockito-junit-jupiter:2.26.0",

21
gradle.properties Normal file
View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Sun Nov 24 16:57:24 EST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip

172
gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
qrecycler/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

34
qrecycler/build.gradle Normal file
View File

@ -0,0 +1,34 @@
import cash.z.ecc.android.Deps
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion Deps.compileSdkVersion
defaultConfig {
minSdkVersion Deps.minSdkVersion
targetSdkVersion Deps.targetSdkVersion
versionCode 4
versionName "1.0.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation Deps.AndroidX.CORE_KTX
implementation Deps.Kotlin.STDLIB
// dependencies specific to this module, not shared with other modules
implementation 'com.google.zxing:core:3.2.1'
}

21
qrecycler/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cash.z.android.qrecycler" >
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

View File

@ -0,0 +1,62 @@
package cash.z.android.qrecycler
import android.graphics.Bitmap
import android.graphics.Color
import android.widget.ImageView
import androidx.core.view.doOnLayout
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType.ERROR_CORRECTION
import com.google.zxing.EncodeHintType.MARGIN
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.*
class QRecycler {
fun load(content: String): Builder {
return Builder(content)
}
// TODO: make this call async such that action can be taken once it is complete
fun encode(builder: Builder) {
builder.target.doOnLayout { measuredView ->
val w = measuredView.width
val h = measuredView.height
val hints = mapOf(ERROR_CORRECTION to builder.errorCorrection, MARGIN to builder.quietZone)
val bitMatrix = QRCodeWriter().encode(builder.content, BarcodeFormat.QR_CODE, w, h, hints)
val pixels = IntArray(w * h)
for (y in 0 until h) {
val offset = y * w
for (x in 0 until w) {
pixels[offset + x] = if (bitMatrix.get(x, y)) Color.BLACK else Color.WHITE
}
}
// TODO: RECYCLE THIS BITMAP MEMORY!!! Do it in a way that is lifecycle-aware and disposes of the memory when the fragment is off-screen
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, w, 0, 0, w, h)
(measuredView as ImageView).setImageBitmap(bitmap)
}
}
inner class Builder(val content: String) {
lateinit var target: ImageView
var errorCorrection: ErrorCorrectionLevel = Q
var quietZone: Int = 4
fun into(imageView: ImageView) {
target = imageView
encode(this)
}
fun withQuietZoneSize(customQuietZone: Int): Builder {
quietZone = customQuietZone
return this
}
fun withCorrectionLevel(level: CorrectionLevel): Builder {
errorCorrection = level.errorCorrectionLevel
return this
}
}
enum class CorrectionLevel(val errorCorrectionLevel: ErrorCorrectionLevel) {
LOW(L), DEFAULT(M), MEDIUM(Q), HIGH(H);
}
}

View File

@ -0,0 +1,8 @@
package cash.z.android.qrecycler
/**
* An interface to allow for plugging in any scanner
*/
interface QScanner {
fun scanBarcode(callback: (Result<String>) -> Unit)
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextureView
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
/>
</merge>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<declare-styleable name="CameraView">
<!--
Set this to true if you want the CameraView to adjust its bounds to preserve the aspect
ratio of its camera preview.
-->
<attr name="android:adjustViewBounds"/>
<!-- Direction the camera faces relative to device screen. -->
<attr name="facing" format="enum">
<!-- The camera device faces the opposite direction as the device's screen. -->
<enum name="back" value="0"/>
<!-- The camera device faces the same direction as the device's screen. -->
<enum name="front" value="1"/>
</attr>
<!-- Aspect ratio of camera preview and pictures. -->
<attr name="aspectRatio" format="string"/>
<!-- Continuous auto focus mode. -->
<attr name="autoFocus" format="boolean"/>
<!-- The flash mode. -->
<attr name="flash" format="enum">
<!-- Flash will not be fired. -->
<enum name="off" value="0"/>
<!--
Flash will always be fired during snapshot.
The flash may also be fired during preview or auto-focus depending on the driver.
-->
<enum name="on" value="1"/>
<!--
Constant emission of light during preview, auto-focus and snapshot.
This can also be used for video recording.
-->
<enum name="torch" value="2"/>
<!--
Flash will be fired automatically when required.
The flash may be fired during preview, auto-focus, or snapshot depending on the
driver.
-->
<enum name="auto" value="3"/>
<!--
Flash will be fired in red-eye reduction mode.
-->
<enum name="redEye" value="4"/>
</attr>
</declare-styleable>
</resources>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<public name="facing" type="attr"/>
<public name="aspectRatio" type="attr"/>
<public name="autoFocus" type="attr"/>
<public name="flash" type="attr"/>
<public name="Widget.CameraView" type="style"/>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">QRecycler</string>
</resources>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="Widget.CameraView" parent="android:Widget">
<item name="android:adjustViewBounds">false</item>
<item name="facing">back</item>
<item name="aspectRatio">4:3</item>
<item name="autoFocus">true</item>
<item name="flash">auto</item>
</style>
</resources>

View File

@ -0,0 +1,17 @@
package cash.z.android.qrecycler;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

2
settings.gradle Normal file
View File

@ -0,0 +1,2 @@
rootProject.name='Zcash Wallet'
include ':app', ':qrecycler'