feat: fallback to zxing-based lib to scan QrCodes
Uses a zxing-based library as a fallback when scanning a Qr-Code and GMS is not available. Closes https://github.com/mastodon/mastodon-android/issues/825
This commit is contained in:
parent
72df72228f
commit
f5517f3d49
|
@ -96,6 +96,7 @@ dependencies {
|
|||
implementation 'com.squareup:otto:1.3.8'
|
||||
implementation 'de.psdev:async-otto:1.0.3'
|
||||
implementation 'com.google.zxing:core:3.5.3'
|
||||
implementation 'com.github.markusfisch:BarcodeScannerView:1.6.0'
|
||||
implementation 'org.microg:safe-parcel:1.5.0'
|
||||
implementation 'org.parceler:parceler-api:1.1.12'
|
||||
annotationProcessor 'org.parceler:parceler:1.1.12'
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
||||
|
||||
|
@ -79,6 +82,7 @@
|
|||
<data android:mimeType="*/*"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".QrCodeScanActivity"/>
|
||||
|
||||
<service android:name=".AudioPlayerService" android:foregroundServiceType="mediaPlayback"/>
|
||||
<service android:name=".NotificationActionHandlerService" android:exported="false"/>
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package org.joinmastodon.android;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import de.markusfisch.android.barcodescannerview.widget.BarcodeScannerView;
|
||||
|
||||
public class QrCodeScanActivity extends Activity{
|
||||
private static final int PERMISSION_RESULT=65537;
|
||||
private BarcodeScannerView scannerView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
UiUtils.setUserPreferredTheme(this);
|
||||
setContentView(R.layout.activity_qr_scan);
|
||||
|
||||
if(this.checkSelfPermission(Manifest.permission.CAMERA)!=PackageManager.PERMISSION_GRANTED){
|
||||
requestPermissions(new String[]{Manifest.permission.CAMERA}, PERMISSION_RESULT);
|
||||
}
|
||||
|
||||
findViewById(R.id.dismiss).setOnClickListener(view -> finish());
|
||||
scannerView=findViewById(R.id.scanner);
|
||||
scannerView.setCropRatio(.75f);
|
||||
scannerView.setOnBarcodeListener(barcode -> {
|
||||
vibrate(scannerView);
|
||||
Intent result=new Intent();
|
||||
result.putExtra("barcode", barcode.getText());
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
|
||||
if(requestCode==PERMISSION_RESULT){
|
||||
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
|
||||
scannerView.openAsync();
|
||||
}else if(!this.shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)){
|
||||
new M3AlertDialogBuilder(this)
|
||||
.setTitle(R.string.permission_required)
|
||||
.setMessage(R.string.camera_permission_to_scan)
|
||||
.setPositiveButton(R.string.open_settings, (dialog, which)->this.startActivity(new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", this.getPackageName(), null))))
|
||||
.setNegativeButton(R.string.cancel, (dialogInterface, i) -> finish())
|
||||
.setOnCancelListener(dialogInterface -> finish())
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void vibrate(View v) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
v.performHapticFeedback(HapticFeedbackConstants.CONFIRM);
|
||||
return;
|
||||
}
|
||||
|
||||
Vibrator vibrator=v.getContext().getSystemService(Vibrator.class);
|
||||
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||
vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
VibrationEffect effect=VibrationEffect.createOneShot(75L, 128);
|
||||
vibrator.vibrate(effect);
|
||||
} else {
|
||||
vibrator.vibrate(75L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
scannerView.openAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
scannerView.close();
|
||||
}
|
||||
}
|
|
@ -53,7 +53,6 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
@ -64,6 +63,7 @@ import com.google.zxing.qrcode.QRCodeWriter;
|
|||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
|
||||
import org.joinmastodon.android.MainActivity;
|
||||
import org.joinmastodon.android.QrCodeScanActivity;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
|
@ -127,7 +127,10 @@ public class ProfileQrCodeFragment extends AppKitFragment{
|
|||
accountID=getArguments().getString("account");
|
||||
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
||||
setCancelable(false);
|
||||
scannerIntent=BarcodeScanner.createIntent(Barcode.FORMAT_QR_CODE, false, true);
|
||||
scannerIntent=GmsClient.isGooglePlayServicesAvailable(getActivity())
|
||||
? BarcodeScanner.createIntent(Barcode.FORMAT_QR_CODE, false, true)
|
||||
: new Intent(getActivity(), QrCodeScanActivity.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -265,11 +268,9 @@ public class ProfileQrCodeFragment extends AppKitFragment{
|
|||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
if(GmsClient.isGooglePlayServicesAvailable(getActivity())){
|
||||
MenuItem item=menu.add(0, 0, 0, R.string.scan_qr_code);
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
item.setIcon(R.drawable.ic_qr_code_scanner_24px);
|
||||
}
|
||||
MenuItem item=menu.add(0, 0, 0, R.string.scan_qr_code);
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
item.setIcon(R.drawable.ic_qr_code_scanner_24px);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -330,11 +331,15 @@ public class ProfileQrCodeFragment extends AppKitFragment{
|
|||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
if(requestCode==SCAN_RESULT && resultCode==Activity.RESULT_OK && BarcodeScanner.isValidResult(data)){
|
||||
Barcode code=BarcodeScanner.getResult(data);
|
||||
if(requestCode==SCAN_RESULT && resultCode==Activity.RESULT_OK){
|
||||
String code=data.getStringExtra("barcode");
|
||||
if(BarcodeScanner.isValidResult(data)){
|
||||
Barcode barcode=BarcodeScanner.getResult(data);
|
||||
code=barcode.rawValue;
|
||||
}
|
||||
if(code!=null){
|
||||
if(code.rawValue.startsWith("https:") || code.rawValue.startsWith("http:")){
|
||||
((MainActivity)getActivity()).handleURL(Uri.parse(code.rawValue), accountID);
|
||||
if(code.startsWith("https:") || code.startsWith("http:")){
|
||||
((MainActivity)getActivity()).handleURL(Uri.parse(code), accountID);
|
||||
dismiss();
|
||||
}else{
|
||||
Toast.makeText(themeWrapper, R.string.link_not_supported, Toast.LENGTH_SHORT).show();
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.MainActivity;
|
||||
import org.joinmastodon.android.QrCodeScanActivity;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.googleservices.GmsClient;
|
||||
|
@ -70,7 +71,9 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop{
|
|||
setRetainInstance(true);
|
||||
|
||||
accountID=getArguments().getString("account");
|
||||
scannerIntent=BarcodeScanner.createIntent(Barcode.FORMAT_QR_CODE, false, true);
|
||||
scannerIntent=GmsClient.isGooglePlayServicesAvailable(getActivity())
|
||||
? BarcodeScanner.createIntent(Barcode.FORMAT_QR_CODE, false, true)
|
||||
: new Intent(getActivity(), QrCodeScanActivity.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -186,11 +189,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop{
|
|||
searchView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
searchScanQR=view.findViewById(R.id.search_scan_qr);
|
||||
if(!GmsClient.isGooglePlayServicesAvailable(getActivity())){
|
||||
searchScanQR.setVisibility(View.GONE);
|
||||
}else{
|
||||
searchScanQR.setOnClickListener(v->openQrScanner());
|
||||
}
|
||||
searchScanQR.setOnClickListener(v->openQrScanner());
|
||||
|
||||
View searchWrap=view.findViewById(R.id.search_wrap);
|
||||
searchWrap.setOutlineProvider(OutlineProviders.roundedRect(28));
|
||||
|
@ -280,11 +279,15 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop{
|
|||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
if(requestCode==SCAN_RESULT && resultCode==Activity.RESULT_OK && BarcodeScanner.isValidResult(data)){
|
||||
Barcode code=BarcodeScanner.getResult(data);
|
||||
if(requestCode==SCAN_RESULT && resultCode==Activity.RESULT_OK){
|
||||
String code=data.getStringExtra("barcode");
|
||||
if(BarcodeScanner.isValidResult(data)){
|
||||
Barcode barcode=BarcodeScanner.getResult(data);
|
||||
code=barcode.rawValue;
|
||||
}
|
||||
if(code!=null){
|
||||
if(code.rawValue.startsWith("https:") || code.rawValue.startsWith("http:")){
|
||||
((MainActivity)getActivity()).handleURL(Uri.parse(code.rawValue), accountID);
|
||||
if(code.startsWith("https:") || code.startsWith("http:")){
|
||||
((MainActivity)getActivity()).handleURL(Uri.parse(code), accountID);
|
||||
}else{
|
||||
Toast.makeText(getActivity(), R.string.link_not_supported, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<de.markusfisch.android.barcodescannerview.widget.BarcodeScannerView
|
||||
android:id="@+id/scanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/dismiss"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|top"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:contentDescription="@string/back"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_arrow_back"
|
||||
android:tint="@android:color/white"
|
||||
android:layout_margin="16dp"/>
|
||||
</FrameLayout>
|
|
@ -233,6 +233,7 @@
|
|||
<string name="download">Download</string>
|
||||
<string name="permission_required">Permission required</string>
|
||||
<string name="storage_permission_to_download">The app needs access to your storage to save this file.</string>
|
||||
<string name="camera_permission_to_scan">The app needs access to your camera to scan.</string>
|
||||
<string name="open_settings">Open settings</string>
|
||||
<string name="error_saving_file">Error saving file</string>
|
||||
<string name="file_saved">File saved</string>
|
||||
|
|
|
@ -4,6 +4,7 @@ dependencyResolutionManagement {
|
|||
google()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
rootProject.name = "Mastodon"
|
||||
|
|
Loading…
Reference in New Issue