diff --git a/README.md b/README.md index 796acb6..f9bfa17 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ Make sure you have Gradle 5.4.x or higher, 5.4.1 is known to work: ./gradlew wrapper --gradle-version=5.4.1 +Or you can use Android Studio on Linux, OS X, or Windows: + + Make sure to install ndkVersion 21.1.6352462 + ## Release Build Process The first time you create a release build you'll need to create a keystore file and prepare a properties file. The @@ -114,7 +118,7 @@ This build can be directly uploaded to Google Play. ### Building a release APK for F-Droid -We should do this & will plan to using [this](https://gitlab.com/fdroid/fdroiddata/blob/master/CONTRIBUTING.md) +This will be pursued once lite wallet functionality exists in the SilentDragonAndroid wallet. ... diff --git a/app/build.gradle b/app/build.gradle index 02c78cd..4275a0d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,18 +1,22 @@ // Copyright 2019-2020 The Hush developers // Released under the GPLv3 +import org.myhush.silentdragon.Deps apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' android { - compileSdkVersion 29 - buildToolsVersion "29.0.3" + ndkVersion "21.1.6352462" + compileSdkVersion Deps.compileSdkVersion + buildToolsVersion Deps.buildToolsVersion defaultConfig { applicationId "org.myhush.silentdragon" - minSdkVersion 17 - targetSdkVersion 29 + minSdkVersion Deps.minSdkVersion + targetSdkVersion Deps.targetSdkVersion + multiDexEnabled true versionCode 4 versionName "0.5.16" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -31,6 +35,15 @@ android { } } + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + } def secretsPropertiesFile = rootProject.file("secrets.properties") @@ -51,19 +64,48 @@ if (secretsPropertiesFile.exists()) { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation Deps.Kotlin.STDLIB + implementation Deps.Kotlin.Coroutines.ANDROID + implementation Deps.Kotlin.Coroutines.CORE + + implementation Deps.AndroidX.APPCOMPAT + implementation Deps.AndroidX.CONSTRAINT_LAYOUT + implementation Deps.AndroidX.CORE_KTX + implementation Deps.AndroidX.FRAGMENT_KTX + implementation Deps.AndroidX.LEGACY + implementation Deps.AndroidX.MULTIDEX + implementation Deps.AndroidX.PAGING + implementation Deps.AndroidX.VECTOR_DRAWABLE + implementation Deps.AndroidX.Navigation.FRAGMENT_KTX + implementation Deps.AndroidX.Navigation.UI_KTX + implementation 'com.google.android.material:material:1.0.0' implementation 'androidmads.library.qrgenearator:QRGenearator:1.0.3' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.beust:klaxon:5.0.1' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.google.android.gms:play-services-vision:17.0.2' - implementation 'androidx.vectordrawable:vectordrawable:1.0.0' implementation 'com.github.joshjdevl.libsodiumjni:libsodium-jni-aar:2.0.1' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.19' + //implementation 'com.journeyapps:zxing-android-embedded:3.6.0' + implementation 'com.journeyapps:zxing-android-embedded:3.6.0@aar' + implementation 'com.google.zxing:core:3.3.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' + + // from Demo App SDK + implementation Deps.Hush.ANDROID_WALLET_PLUGINS + implementation Deps.Hush.Sdk.MAINNET + implementation Deps.Hush.KOTLIN_BIP39 + implementation Deps.Grpc.ANDROID + implementation Deps.Grpc.OKHTTP + implementation Deps.Grpc.PROTOBUG + implementation Deps.Grpc.STUB + implementation 'javax.annotation:javax.annotation-api:1.3.2' + implementation Deps.AndroidX.Room.ROOM_KTX + implementation Deps.AndroidX.PAGING + implementation 'com.google.guava:guava:27.0.1-android' + kapt Deps.AndroidX.Room.ROOM_COMPILER + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ce58d5d..0217e12 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" package="org.myhush.silentdragon"> + @@ -21,7 +22,7 @@ tools:replace="android:allowBackup"> @@ -30,7 +31,7 @@ @@ -40,7 +41,7 @@ @@ -51,7 +52,8 @@ + android:screenOrientation="fullSensor" + tools:replace="screenOrientation" /> - diff --git a/app/src/main/java/org/myhush/silentdragon/MainActivity.kt b/app/src/main/java/org/myhush/silentdragon/MainActivity.kt index d0833f3..bb7c152 100644 --- a/app/src/main/java/org/myhush/silentdragon/MainActivity.kt +++ b/app/src/main/java/org/myhush/silentdragon/MainActivity.kt @@ -10,11 +10,6 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Handler -import android.os.StrictMode -import androidx.constraintlayout.widget.ConstraintLayout -import com.google.android.material.snackbar.Snackbar -import androidx.core.text.HtmlCompat -import androidx.appcompat.app.AppCompatActivity import android.text.Html import android.util.Log import android.view.Menu @@ -23,14 +18,19 @@ import android.widget.Button import android.widget.ScrollView import android.widget.TextView import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.text.HtmlCompat import com.beust.klaxon.Klaxon +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.content_main.* import org.myhush.silentdragon.DataModel.ConnectionStatus import org.myhush.silentdragon.DataModel.connStatus +import org.myhush.silentdragon.ui.AboutActivity +import org.myhush.silentdragon.ui.SettingsActivity import java.text.DecimalFormat - class MainActivity : AppCompatActivity(), TransactionItemFragment.OnFragmentInteractionListener, UnconfirmedTxItemFragment.OnFragmentInteractionListener { @@ -51,7 +51,6 @@ class MainActivity : AppCompatActivity(), // When creating, clear all the data first setMainStatus("") - DataModel.init() btnConnect.setOnClickListener { @@ -87,7 +86,6 @@ class MainActivity : AppCompatActivity(), txtMainBalanceUSD.setOnClickListener { - if(DataModel.selectedCurrency == "BTC") Toast.makeText(applicationContext, "1 HUSH = ${DataModel.currencySymbols[DataModel.selectedCurrency]}${DecimalFormat(" #,##0.00000000") .format(DataModel.currencyValues[DataModel.selectedCurrency])}", Toast.LENGTH_LONG).show() @@ -130,7 +128,6 @@ class MainActivity : AppCompatActivity(), lblBalance.text = "" txtMainBalanceUSD.text = "" txtMainBalance.text = status - } @SuppressLint("SetTextI18n") @@ -297,14 +294,12 @@ class MainActivity : AppCompatActivity(), R.id.action_refresh -> { swiperefresh.isRefreshing = true ConnectionManager.refreshAllData() - return true } else -> super.onOptionsItemSelected(item) } } - var mReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // We've received a signal @@ -346,28 +341,35 @@ class MainActivity : AppCompatActivity(), } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when(requestCode) { + when (requestCode) { QrReaderActivity.REQUEST_CONNDATA -> { if (resultCode == Activity.RESULT_OK) { Log.i(TAG, "Main Activity got result for QrCode: ${data?.dataString}") // Check to make sure that the result is an actual address if (!(data?.dataString ?: "").startsWith("ws")) { - Toast.makeText(applicationContext, - getString(R.string.is_not_a_valid_connection_string, data?.dataString), Toast.LENGTH_SHORT).show() + Toast.makeText( + applicationContext, + getString(R.string.is_not_a_valid_connection_string, data?.dataString), + Toast.LENGTH_SHORT + ).show() return } val conComponents = data?.dataString?.split(",") if (conComponents?.size ?: 0 < 2 || conComponents?.size ?: 0 > 3) { - Toast.makeText(applicationContext, - getString(R.string.is_not_a_valid_connection_string, data?.dataString), Toast.LENGTH_SHORT).show() + Toast.makeText( + applicationContext, + getString(R.string.is_not_a_valid_connection_string, data?.dataString), + Toast.LENGTH_SHORT + ).show() return } val conString = conComponents!![0] val secretHex = conComponents[1] - val allowInternetConnections = if (conComponents.size == 3) conComponents[2] == "1" else false + val allowInternetConnections = + if (conComponents.size == 3) conComponents[2] == "1" else false DataModel.setSecretHex(secretHex) DataModel.setConnString( @@ -395,7 +397,5 @@ class MainActivity : AppCompatActivity(), updateUI(true) } - private val TAG = "MainActivity" - } diff --git a/app/src/main/java/org/myhush/silentdragon/QrReaderActivity.kt b/app/src/main/java/org/myhush/silentdragon/QrReaderActivity.kt index e03cb2f..1b56f8c 100644 --- a/app/src/main/java/org/myhush/silentdragon/QrReaderActivity.kt +++ b/app/src/main/java/org/myhush/silentdragon/QrReaderActivity.kt @@ -2,29 +2,26 @@ package org.myhush.silentdragon import android.app.Activity +import android.app.AlertDialog import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.appcompat.app.AppCompatActivity +import android.text.InputType import android.util.Log import android.view.Menu import android.view.MenuItem -import android.view.SurfaceHolder -import android.view.SurfaceView -import com.google.android.gms.vision.CameraSource -import com.google.android.gms.vision.Detector -import com.google.android.gms.vision.barcode.Barcode -import com.google.android.gms.vision.barcode.BarcodeDetector -import kotlinx.android.synthetic.main.activity_qr_reader.* -import java.io.IOException -import android.app.AlertDialog -import android.text.InputType import android.widget.EditText +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.google.zxing.ResultPoint +import com.journeyapps.barcodescanner.BarcodeCallback +import com.journeyapps.barcodescanner.BarcodeResult +import com.journeyapps.barcodescanner.CaptureManager +import kotlinx.android.synthetic.main.activity_qr_reader.* class QrReaderActivity : AppCompatActivity() { + lateinit var captureManager: CaptureManager companion object { const val REQUEST_ADDRESS = 1 @@ -43,7 +40,26 @@ class QrReaderActivity : AppCompatActivity() { lblErrorMsg.text = "" - setupCamera() + btnQrCodeCancel.setOnClickListener { + setResult(Activity.RESULT_CANCELED) + finish() + } + + captureManager = CaptureManager(this, barcodeView) + captureManager.initializeFromIntent(intent, savedInstanceState) + + barcodeView.decodeSingle(object: BarcodeCallback{ + override fun barcodeResult(result: BarcodeResult?) { + result?.let { + if (result.text != null) { + processQrCodeText(result.text) + } + } + } + override fun possibleResultPoints(resultPoints: MutableList?) { + } + }) + } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -92,63 +108,19 @@ class QrReaderActivity : AppCompatActivity() { } } - private fun setupCamera() { - val cameraView = findViewById(R.id.camera_view) - - val barcodeDetector = BarcodeDetector.Builder(this).setBarcodeFormats(Barcode.QR_CODE).build() - val cameraSource = CameraSource.Builder(this, barcodeDetector) - .setAutoFocusEnabled(true) - .setRequestedPreviewSize(640, 480) - .build() - - - cameraView.holder.addCallback(object : SurfaceHolder.Callback { - override fun surfaceCreated(holder: SurfaceHolder) { - try { - if (ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this@QrReaderActivity, arrayOf(android.Manifest.permission.CAMERA), 50) - } else { - cameraSource.start(cameraView.holder) - - val w = cameraView.width - val h = cameraView.height - val scale = cameraSource.previewSize.width.toDouble() / cameraSource.previewSize.height.toDouble() - - val scaleWidth = (h.toDouble() / scale).toInt() - - cameraView.layout((w - scaleWidth)/2, 0, scaleWidth , h) - println("Preview size: ${cameraSource.previewSize}") - } - } catch (ie: IOException) { - Log.e("CAMERA SOURCE", ie.toString()) - } - } - - override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {} - - override fun surfaceDestroyed(holder: SurfaceHolder) { - cameraSource.stop() - } - }) - - btnQrCodeCancel.setOnClickListener { - setResult(Activity.RESULT_CANCELED) - finish() - } + override fun onPause() { + super.onPause() + captureManager.onPause() + } - barcodeDetector.setProcessor(object : Detector.Processor { - override fun release() {} + override fun onResume() { + super.onResume() + captureManager.onResume() + } - override fun receiveDetections(detections: Detector.Detections) { - val barcodes = detections.detectedItems - if (barcodes.size() != 0) { - runOnUiThread { - val barcodeInfo = barcodes.valueAt(0).displayValue - processText(barcodeInfo) - } - } - } - }) + override fun onDestroy() { + super.onDestroy() + captureManager.onDestroy() } private fun processText(barcodeInfo: String) { @@ -174,15 +146,13 @@ class QrReaderActivity : AppCompatActivity() { err = err.substring(0, 22) + "...." + err.substring(err.length - 22, err.length) } lblErrorMsg.text = getString(R.string.is_not_a_valid_hush_address, err) - return } - // The data seems valid, so return it. val data = Intent() - // Payment URIs are often formatted as "hush:", but this casuses parsing problems. + // Payment URIs are often formatted as "hush:", but this causes parsing problems. // So change it to hush://, so that it parses properly if (barcodeInfo.startsWith("hush:") && !barcodeInfo.startsWith("hush://")) { data.data = Uri.parse(barcodeInfo.replaceFirst("hush:", "hush://")) @@ -194,5 +164,22 @@ class QrReaderActivity : AppCompatActivity() { finish() } + private fun processQrCodeText(qrcodeInfo: String) { + if (qrcodeInfo.startsWith("ws")) { + Log.i(TAG, "It's a ws connection") + //Toast.makeText(this, "YEAH: " + qrcodeInfo, Toast.LENGTH_SHORT).show(); + + val data = Intent() // The data seems valid, so return it + data.data = Uri.parse(qrcodeInfo) + setResult(Activity.RESULT_OK, data) + finish() + } else { + Log.i(TAG, "Not a ws connection") + //Toast.makeText(this, "Not a ws connection", Toast.LENGTH_SHORT).show(); + setResult(Activity.RESULT_CANCELED) + finish() + } + } + private val TAG = "QrReader" } diff --git a/app/src/main/java/org/myhush/silentdragon/AboutActivity.kt b/app/src/main/java/org/myhush/silentdragon/ui/AboutActivity.kt similarity index 81% rename from app/src/main/java/org/myhush/silentdragon/AboutActivity.kt rename to app/src/main/java/org/myhush/silentdragon/ui/AboutActivity.kt index 2a084cc..d37d794 100644 --- a/app/src/main/java/org/myhush/silentdragon/AboutActivity.kt +++ b/app/src/main/java/org/myhush/silentdragon/ui/AboutActivity.kt @@ -1,9 +1,11 @@ // Copyright 2019-2020 The Hush developers -package org.myhush.silentdragon +package org.myhush.silentdragon.ui import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_about.lblVersionName +import org.myhush.silentdragon.BuildConfig +import org.myhush.silentdragon.R class AboutActivity : AppCompatActivity() { diff --git a/app/src/main/java/org/myhush/silentdragon/SettingsActivity.kt b/app/src/main/java/org/myhush/silentdragon/ui/SettingsActivity.kt similarity index 80% rename from app/src/main/java/org/myhush/silentdragon/SettingsActivity.kt rename to app/src/main/java/org/myhush/silentdragon/ui/SettingsActivity.kt index 93b47a3..a25a168 100644 --- a/app/src/main/java/org/myhush/silentdragon/SettingsActivity.kt +++ b/app/src/main/java/org/myhush/silentdragon/ui/SettingsActivity.kt @@ -1,5 +1,5 @@ // Copyright 2019-2020 The Hush developers -package org.myhush.silentdragon +package org.myhush.silentdragon.ui import android.content.SharedPreferences import androidx.appcompat.app.AppCompatActivity @@ -9,6 +9,10 @@ import android.widget.Adapter import android.widget.AdapterView import android.widget.ArrayAdapter import kotlinx.android.synthetic.main.activity_settings.* +import org.myhush.silentdragon.ConnectionManager +import org.myhush.silentdragon.DataModel +import org.myhush.silentdragon.R +import org.myhush.silentdragon.SilentDragonApp class SettingsActivity : AppCompatActivity() { @@ -19,7 +23,10 @@ class SettingsActivity : AppCompatActivity() { updateUI() btnDisconnect.setOnClickListener { - DataModel.setConnString(null, applicationContext) + DataModel.setConnString( + null, + applicationContext + ) DataModel.clear() ConnectionManager.closeConnection() @@ -47,7 +54,9 @@ class SettingsActivity : AppCompatActivity() { var pref: SharedPreferences = getSharedPreferences("MainFile",0) var editor: SharedPreferences.Editor = pref.edit() - editor.putString("currency", DataModel.selectedCurrency) + editor.putString("currency", + DataModel.selectedCurrency + ) editor.commit() } @@ -79,11 +88,15 @@ class SettingsActivity : AppCompatActivity() { fun updateUI() { fillSpinner() - txtSettingsConnString.text = DataModel.getConnString(SilentDragonApp.appContext!!) + txtSettingsConnString.text = DataModel.getConnString( + SilentDragonApp.appContext!! + ) ?: getString(R.string.not_connected) chkDisallowInternet.isChecked = !DataModel.getGlobalAllowInternet() - lblServerVersion.text = DataModel.mainResponseData?.serverversion ?: getString(R.string.not_connected) + lblServerVersion.text = DataModel.mainResponseData?.serverversion ?: getString( + R.string.not_connected + ) } } diff --git a/app/src/main/java/org/myhush/silentdragon/SplashActivity.kt b/app/src/main/java/org/myhush/silentdragon/ui/SplashActivity.kt similarity index 79% rename from app/src/main/java/org/myhush/silentdragon/SplashActivity.kt rename to app/src/main/java/org/myhush/silentdragon/ui/SplashActivity.kt index c099031..9cfd0a2 100644 --- a/app/src/main/java/org/myhush/silentdragon/SplashActivity.kt +++ b/app/src/main/java/org/myhush/silentdragon/ui/SplashActivity.kt @@ -1,11 +1,13 @@ // Copyright 2019-2020 The Hush developers // Released under the GPLv3 -package org.myhush.silentdragon +package org.myhush.silentdragon.ui import androidx.appcompat.app.AppCompatActivity import android.content.Intent import android.os.Bundle import android.os.Handler +import org.myhush.silentdragon.MainActivity +import org.myhush.silentdragon.R class SplashActivity : AppCompatActivity() { @@ -18,7 +20,7 @@ class SplashActivity : AppCompatActivity() { Handler().postDelayed({ // This method will be executed once the timer is over - startActivity(Intent(this,MainActivity::class.java)) + startActivity(Intent(this, MainActivity::class.java)) // close this activity finish() diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 44e87ae..1a7f629 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -5,7 +5,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="org.myhush.silentdragon.AboutActivity"> + tools:context="org.myhush.silentdragon.ui.AboutActivity"> - + + + android:layout_height="0dp" + app:layout_constraintBottom_toTopOf="@+id/txtQrCodeHelp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:zxing_scanner_layout="@layout/custom_qrcode_layout" /> - - + android:id="@+id/txtQrCodeHelp" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:layout_marginBottom="16dp" + android:text="@string/how_to_connect_sd2sda" + android:textAlignment="center" + android:textColor="@android:color/white" + app:layout_constraintBottom_toTopOf="@+id/btnQrCodeCancel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> +