add ability to scan QR codes

This commit is contained in:
pokkst 2022-09-12 12:35:26 -05:00
parent fcadb39b76
commit 4089e22bc5
No known key found for this signature in database
GPG key ID: 90C2ED85E67A50FF
7 changed files with 104 additions and 29 deletions

View file

@ -108,8 +108,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_9
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_9
} }
namespace 'com.m2049r.xmrwallet' namespace 'com.m2049r.xmrwallet'
buildFeatures { buildFeatures {
@ -135,7 +135,7 @@ dependencies {
implementation 'com.google.android.material:material:1.6.0' implementation 'com.google.android.material:material:1.6.0'
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation "com.squareup.okhttp3:okhttp:4.9.3" implementation "com.squareup.okhttp3:okhttp:4.9.3"
implementation "io.github.rburgst:okhttp-digest:2.6" implementation "io.github.rburgst:okhttp-digest:2.6"
implementation "com.jakewharton.timber:timber:5.0.1" implementation "com.jakewharton.timber:timber:5.0.1"

View file

@ -1,32 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.m2049r.xmrwallet"> package="com.m2049r.xmrwallet">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries>
<intent>
<action android:name="org.torproject.android.intent.action.START" />
</intent>
<intent>
<action android:name="org.torproject.android.intent.action.STATUS" />
</intent>
<intent>
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent>
<intent>
<action android:name="org.torproject.android.REQUEST_V3_ONION_SERVICE" />
</intent>
<package android:name="org.torproject.android" />
</queries>
<application <application
android:name=".MoneroApplication" android:name=".MoneroApplication"
@ -46,6 +25,11 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="portrait"
tools:replace="android:screenOrientation"
android:stateNotNeeded="true"/>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider" android:authorities="${applicationId}.fileprovider"

View file

@ -1,9 +1,14 @@
package com.m2049r.xmrwallet.fragment.dialog; package com.m2049r.xmrwallet.fragment.dialog;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.zxing.client.android.Intents;
import com.journeyapps.barcodescanner.ScanContract;
import com.journeyapps.barcodescanner.ScanOptions;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.PendingTransaction; import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
@ -20,12 +25,32 @@ import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import java.util.List;
public class SendBottomSheetDialog extends BottomSheetDialogFragment { public class SendBottomSheetDialog extends BottomSheetDialogFragment {
private final ActivityResultLauncher<ScanOptions> barcodeLauncher = registerForActivityResult(new ScanContract(),
result -> {
if(result.getContents() != null) {
pasteAddress(result.getContents());
}
});
private final ActivityResultLauncher<String> cameraPermissionsLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(),
granted -> {
if(granted) {
onScan();
} else {
Toast.makeText(getActivity(), getString(R.string.no_camera_permission), Toast.LENGTH_SHORT).show();
}
});
private MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false); private MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false);
public LiveData<Boolean> sendingMax = _sendingMax; public LiveData<Boolean> sendingMax = _sendingMax;
private MutableLiveData<PendingTransaction> _pendingTransaction = new MutableLiveData<>(null); private MutableLiveData<PendingTransaction> _pendingTransaction = new MutableLiveData<>(null);
@ -41,6 +66,7 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
private Button sendButton; private Button sendButton;
private Button sendMaxButton; private Button sendMaxButton;
private ImageButton pasteAddressImageButton; private ImageButton pasteAddressImageButton;
private ImageButton scanAddressImageButton;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -51,6 +77,7 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton); pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
scanAddressImageButton = view.findViewById(R.id.scan_address_imagebutton);
sendMaxButton = view.findViewById(R.id.send_max_button); sendMaxButton = view.findViewById(R.id.send_max_button);
addressEditText = view.findViewById(R.id.address_edittext); addressEditText = view.findViewById(R.id.address_edittext);
amountEditText = view.findViewById(R.id.amount_edittext); amountEditText = view.findViewById(R.id.amount_edittext);
@ -62,7 +89,14 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
amountTextView = view.findViewById(R.id.amount_pending_textview); amountTextView = view.findViewById(R.id.amount_pending_textview);
pasteAddressImageButton.setOnClickListener(view1 -> { pasteAddressImageButton.setOnClickListener(view1 -> {
addressEditText.setText(Helper.getClipBoardText(view.getContext())); Context ctx = getContext();
if(ctx != null) {
pasteAddress(Helper.getClipBoardText(getContext()));
}
});
scanAddressImageButton.setOnClickListener(view1 -> {
onScan();
}); });
sendMaxButton.setOnClickListener(view1 -> { sendMaxButton.setOnClickListener(view1 -> {
@ -127,6 +161,17 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
}); });
} }
private void onScan() {
if (Helper.getCameraPermission(getActivity(), cameraPermissionsLauncher)) {
ScanOptions options = new ScanOptions();
options.setBeepEnabled(false);
options.setOrientationLocked(true);
options.setDesiredBarcodeFormats(List.of(Intents.Scan.QR_CODE_MODE));
options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN);
barcodeLauncher.launch(options);
}
}
private void sendTx(PendingTransaction pendingTx) { private void sendTx(PendingTransaction pendingTx) {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
boolean success = TxService.getInstance().sendTx(pendingTx); boolean success = TxService.getInstance().sendTx(pendingTx);
@ -171,6 +216,7 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
createButton.setVisibility(View.GONE); createButton.setVisibility(View.GONE);
sendMaxButton.setVisibility(View.GONE); sendMaxButton.setVisibility(View.GONE);
pasteAddressImageButton.setVisibility(View.GONE); pasteAddressImageButton.setVisibility(View.GONE);
scanAddressImageButton.setVisibility(View.GONE);
feeTextView.setVisibility(View.VISIBLE); feeTextView.setVisibility(View.VISIBLE);
addressTextView.setVisibility(View.VISIBLE); addressTextView.setVisibility(View.VISIBLE);
amountTextView.setVisibility(View.VISIBLE); amountTextView.setVisibility(View.VISIBLE);
@ -182,9 +228,20 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
createButton.setVisibility(View.VISIBLE); createButton.setVisibility(View.VISIBLE);
sendMaxButton.setVisibility(View.VISIBLE); sendMaxButton.setVisibility(View.VISIBLE);
pasteAddressImageButton.setVisibility(View.VISIBLE); pasteAddressImageButton.setVisibility(View.VISIBLE);
scanAddressImageButton.setVisibility(View.VISIBLE);
feeTextView.setVisibility(View.GONE); feeTextView.setVisibility(View.GONE);
addressTextView.setVisibility(View.GONE); addressTextView.setVisibility(View.GONE);
amountTextView.setVisibility(View.GONE); amountTextView.setVisibility(View.GONE);
} }
} }
private void pasteAddress(String address) {
String modifiedAddress = address.replace("monero:", "").split("\\?")[0];
boolean isValid = Wallet.isAddressValid(modifiedAddress);
if(isValid) {
addressEditText.setText(modifiedAddress);
} else {
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
}
}
} }

View file

@ -41,6 +41,7 @@ import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@ -114,6 +115,21 @@ public class Helper {
} }
} }
static public boolean getCameraPermission(Activity context, ActivityResultLauncher<String> launcher) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (context.checkSelfPermission(Manifest.permission.CAMERA)
== PackageManager.PERMISSION_DENIED) {
Timber.w("Permission denied for CAMERA - requesting it");
launcher.launch(Manifest.permission.CAMERA);
return false;
} else {
return true;
}
} else {
return true;
}
}
static public File getWalletFile(Context context, String aWalletName) { static public File getWalletFile(Context context, String aWalletName) {
File walletDir = getWalletRoot(context); File walletDir = getWalletRoot(context);
File f = new File(walletDir, aWalletName); File f = new File(walletDir, aWalletName);

View file

@ -4,6 +4,6 @@
android:viewportWidth="30.0" android:viewportWidth="30.0"
android:viewportHeight="30.0"> android:viewportHeight="30.0">
<path <path
android:fillColor="?attr/colorPrimaryVariant" android:fillColor="@color/oled_textColorPrimary"
android:pathData="M26.667,27.996L27.991,26.667L27.991,20L30,20L30,26.667C30,28.5 28.5,30 26.667,30L20,30L20,27.996L26.667,27.996ZM3.333,27.996L10,27.996L10,30L3.333,30C1.5,30 0,28.5 0,26.667L0,20L2.009,20L2.009,26.667L3.333,27.996ZM26.667,2.004L20,2.004L20,0L26.667,0C28.5,0 30,1.5 30,3.333L30,10L27.991,10L27.991,3.333L26.667,2.004ZM3.333,2.004L2.009,3.333L2.009,10L0,10L0,3.333C0,1.5 1.5,0 3.333,0L10,0L10,2.004L3.333,2.004Z" /> android:pathData="M26.667,27.996L27.991,26.667L27.991,20L30,20L30,26.667C30,28.5 28.5,30 26.667,30L20,30L20,27.996L26.667,27.996ZM3.333,27.996L10,27.996L10,30L3.333,30C1.5,30 0,28.5 0,26.667L0,20L2.009,20L2.009,26.667L3.333,27.996ZM26.667,2.004L20,2.004L20,0L26.667,0C28.5,0 30,1.5 30,3.333L30,10L27.991,10L27.991,3.333L26.667,2.004ZM3.333,2.004L2.009,3.333L2.009,10L0,10L0,3.333C0,1.5 1.5,0 3.333,0L10,0L10,2.004L3.333,2.004Z" />
</vector> </vector>

View file

@ -48,15 +48,32 @@
android:id="@+id/paste_address_imagebutton" android:id="@+id/paste_address_imagebutton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minWidth="48dp"
android:minHeight="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/address_edittext"
app:layout_constraintEnd_toStartOf="@id/scan_address_imagebutton"
app:layout_constraintStart_toEndOf="@id/address_edittext"
app:layout_constraintTop_toTopOf="@id/address_edittext"
tools:ignore="SpeakableTextPresentCheck"
tools:visibility="gone" />
<ImageButton
android:id="@+id/scan_address_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:minWidth="48dp" android:minWidth="48dp"
android:minHeight="48dp" android:minHeight="48dp"
android:padding="8dp" android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp" android:src="@drawable/ic_scan"
app:layout_constraintBottom_toBottomOf="@id/address_edittext" app:layout_constraintBottom_toBottomOf="@id/address_edittext"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/address_edittext" app:layout_constraintStart_toEndOf="@id/paste_address_imagebutton"
app:layout_constraintTop_toTopOf="@id/address_edittext" app:layout_constraintTop_toTopOf="@id/address_edittext"
tools:ignore="SpeakableTextPresentCheck" tools:ignore="SpeakableTextPresentCheck"
tools:visibility="gone" /> tools:visibility="gone" />

View file

@ -571,4 +571,5 @@
<string name="creating_tx">Creating transaction…</string> <string name="creating_tx">Creating transaction…</string>
<string name="sending_tx">Sending transaction…</string> <string name="sending_tx">Sending transaction…</string>
<string name="sent_tx">Sent transaction!</string> <string name="sent_tx">Sent transaction!</string>
<string name="no_camera_permission">No camera permission</string>
</resources> </resources>