diff --git a/android/build.gradle b/android/build.gradle index 631ea93..6af325a 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,8 +1,22 @@ apply plugin: 'com.android.library' + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.google.gms:google-services:3.0.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" + compileSdkVersion 25 + buildToolsVersion "25.0.0" defaultConfig { minSdkVersion 16 @@ -20,7 +34,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.0.1' + compile 'com.android.support:appcompat-v7:25.0.0' compile 'com.facebook.react:react-native:+' - compile 'com.google.zxing:core:3.2.0' + compile 'com.google.zxing:core:+' + compile 'com.google.android.gms:play-services-vision:10.2.0' } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 0b6bfea..f3c7f75 100755 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,9 @@ + + + + diff --git a/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImage.java b/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImage.java index f873951..98d7dd5 100755 --- a/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImage.java +++ b/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImage.java @@ -1,7 +1,14 @@ package com.remobile.qrcodeLocalImage; +import android.os.Environment; +import android.util.Log; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.content.Context; +import android.net.Uri; +import android.database.Cursor; +import android.provider.MediaStore; +import android.util.SparseArray; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; @@ -10,22 +17,39 @@ import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.RGBLuminanceSource; +import com.google.zxing.LuminanceSource; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.QRCodeReader; +import com.google.android.gms.vision.barcode.*; +import com.google.android.gms.vision.Frame; + +import java.io.Reader; +import java.io.FileOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Hashtable; +import static java.security.AccessController.getContext; + public class RCTQRCodeLocalImage extends ReactContextBaseJavaModule { + + private ReactApplicationContext mReactContext; + + private static final int RGB_MASK = 0x00FFFFFF; + public RCTQRCodeLocalImage(ReactApplicationContext reactContext) { super(reactContext); + mReactContext = reactContext; } + private static final String TAG = "QR"; + @Override public String getName() { return "RCTQRCodeLocalImage"; @@ -33,46 +57,83 @@ public String getName() { @ReactMethod public void decode(String path, Callback callback) { - Hashtable hints = new Hashtable(); - hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码 - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; // 先获取原大小 - options.inJustDecodeBounds = false; // 获取新的大小 - - int sampleSize = (int) (options.outHeight / (float) 200); - - if (sampleSize <= 0) - sampleSize = 1; - options.inSampleSize = sampleSize; - Bitmap scanBitmap = null; - if (path.startsWith("http://")||path.startsWith("https://")) { - scanBitmap = this.getbitmap(path); - } else { - scanBitmap = BitmapFactory.decodeFile(path, options); - } - if (scanBitmap == null) { - callback.invoke("cannot load image"); - return; - } - int[] intArray = new int[scanBitmap.getWidth()*scanBitmap.getHeight()]; - scanBitmap.getPixels(intArray, 0, scanBitmap.getWidth(), 0, 0, scanBitmap.getWidth(), scanBitmap.getHeight()); - - RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap.getWidth(), scanBitmap.getHeight(), intArray); - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); - QRCodeReader reader = new QRCodeReader(); try { - Result result = reader.decode(bitmap, hints); - if (result == null) { - callback.invoke("image format error"); + Uri mediaUri = Uri.parse(path); + String realPath = getRealPathFromUri(mReactContext, mediaUri); + Hashtable hints = new Hashtable(); + hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码 + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; // 先获取原大小 + options.inJustDecodeBounds = false; // 获取新的大小 + + int sampleSize = (int) (options.outHeight / (float) 200); + + if (sampleSize <= 0) + sampleSize = 1; + options.inSampleSize = sampleSize; + Bitmap scanBitmap = null; + if (path.startsWith("http://")||path.startsWith("https://")) { + scanBitmap = this.getbitmap("https://instagram.fmvd1-1.fna.fbcdn.net/t51.2885-15/e35/18722826_1379673005443137_1152886071126654976_n.jpg"); } else { - callback.invoke(null, result.toString()); + scanBitmap = BitmapFactory.decodeFile(realPath, options); } + if (scanBitmap == null) { + callback.invoke("cannot load image"); + return; + } + + Bitmap scanInvertBitmap = invert(scanBitmap); + // https://code.tutsplus.com/tutorials/reading-qr-codes-using-the-mobile-vision-api--cms-24680 + + int[] intArray = new int[scanInvertBitmap.getWidth()*scanInvertBitmap.getHeight()]; + scanInvertBitmap.getPixels(intArray, 0, scanInvertBitmap.getWidth(), 0, 0, scanInvertBitmap.getWidth(), scanInvertBitmap.getHeight()); + LuminanceSource source = new RGBLuminanceSource(scanInvertBitmap.getWidth(), scanInvertBitmap.getHeight(), intArray); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + QRCodeReader reader = new QRCodeReader(); + + try { + Result result = reader.decode(bitmap, hints); + Log.d(TAG, "result - " + result); + if (result == null) { + callback.invoke("Image without qr"); + } else { + callback.invoke(null, result.toString()); + } + + } catch (Exception e) { + Log.d(TAG, "Error - " + e); + callback.invoke("Decode error"); + } } catch (Exception e) { - callback.invoke("decode error"); + Log.d(TAG, "Error GENERIC - " + e); + callback.invoke("Life error"); } } + public Bitmap invert(Bitmap original) { + // Create mutable Bitmap to invert, argument true makes it mutable + Bitmap inversion = original.copy(Bitmap.Config.ARGB_8888, true); + + // Get info about Bitmap + int width = inversion.getWidth(); + int height = inversion.getHeight(); + int pixels = width * height; + + // Get original pixels + int[] pixel = new int[pixels]; + inversion.getPixels(pixel, 0, width, 0, 0, width, height); + + // Modify pixels + for (int i = 0; i < pixels; i++) + pixel[i] ^= RGB_MASK; + inversion.setPixels(pixel, 0, width, 0, 0, width, height); + // Return inverted Bitmap +// saveImage(inversion, "caro"); + return inversion; + } + public static Bitmap getbitmap(String imageUri) { Bitmap bitmap = null; try { @@ -90,6 +151,47 @@ public static Bitmap getbitmap(String imageUri) { e.printStackTrace(); bitmap = null; } + return bitmap; } + +// private void saveImage(Bitmap finalBitmap, String image_name) { +// +// // Find the SD Card path +// File filepath = Environment.getExternalStorageDirectory(); +// +// // Create a new folder in SD Card +// File myDir = new File(filepath.getAbsolutePath() +// + "/WhatSappIMG/"); +// myDir.mkdirs(); +// String fname = "/Image-" + image_name+ ".jpg"; +// File file = new File(myDir, fname); +// if (file.exists()) file.delete(); +// Log.d(TAG, "LOAD " + myDir + fname); +// try { +// FileOutputStream out = new FileOutputStream(file); +// finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out); +// out.flush(); +// out.close(); +// Log.d(TAG, "SaveImage "); +// } catch (Exception e) { +// e.printStackTrace(); +// Log.d(TAG, "Error saveImage - " + e); +// } +// } + + public static String getRealPathFromUri(Context context, Uri contentUri) { + Cursor cursor = null; + try { + String[] proj = { MediaStore.Images.Media.DATA }; + cursor = context.getContentResolver().query(contentUri, proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + cursor.moveToFirst(); + return cursor.getString(column_index); + } finally { + if (cursor != null) { + cursor.close(); + } + } + } } diff --git a/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImagePackage.java b/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImagePackage.java index 01a6e3f..702921d 100755 --- a/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImagePackage.java +++ b/android/src/main/java/com/remobile/qrcodeLocalImage/RCTQRCodeLocalImagePackage.java @@ -18,11 +18,6 @@ public List createNativeModules(ReactApplicationContext reactConte ); } - @Override - public List> createJSModules() { - return Collections.emptyList(); - } - @Override public List createViewManagers(ReactApplicationContext reactContext) { return Arrays.asList(); diff --git a/ios/RCTQRCodeLocalImage/RCTQRCodeLocalImage.m b/ios/RCTQRCodeLocalImage/RCTQRCodeLocalImage.m old mode 100755 new mode 100644 index 03453c7..5e63dbd --- a/ios/RCTQRCodeLocalImage/RCTQRCodeLocalImage.m +++ b/ios/RCTQRCodeLocalImage/RCTQRCodeLocalImage.m @@ -10,44 +10,71 @@ #import #import #import "RCTQRCodeLocalImage.h" +#import @implementation RCTQRCodeLocalImage RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(decode:(NSString *)path callback:(RCTResponseSenderBlock)callback) { - UIImage *srcImage; - if ([path hasPrefix:@"http://"] || [path hasPrefix:@"https://"]) { - srcImage = [UIImage imageWithData: [NSData dataWithContentsOfURL:[NSURL URLWithString: path]]]; - } else { - srcImage = [[UIImage alloc] initWithContentsOfFile:path]; - } - if (nil==srcImage){ - NSLog(@"PROBLEM! IMAGE NOT LOADED\n"); - callback(@[RCTMakeError(@"IMAGE NOT LOADED!", nil, nil)]); - return; - } - NSLog(@"OK - IMAGE LOADED\n"); - NSDictionary *detectorOptions = @{@"CIDetectorAccuracy": @"CIDetectorAccuracyHigh"}; - CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:detectorOptions]; - CIImage *image = [CIImage imageWithCGImage:srcImage.CGImage]; - NSArray *features = [detector featuresInImage:image]; - if (0==features.count) { - NSLog(@"PROBLEM! Feature size is zero!\n"); - callback(@[RCTMakeError(@"Feature size is zero!", nil, nil)]); - return; - } - CIQRCodeFeature *feature = [features firstObject]; - NSString *result = feature.messageString; - NSLog(@"result: %@", result); + ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) + { + ALAssetRepresentation *rep = [myasset defaultRepresentation]; + float width = [rep dimensions].width; + float height = [rep dimensions].height; +// CGImageRef iref = CGImageCreateWithImageInRect([rep fullResolutionImage], CGRectMake(width/2, 0, width/2, height)); + CGImageRef iref = rep.fullScreenImage; + if (nil==rep){ + NSLog(@"PROBLEM! IMAGE NOT LOADED\n"); + callback(@[RCTMakeError(@"IMAGE NOT LOADED!", nil, nil)]); + return; + } + +// Write image to test + +// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); +// NSString *fileName = [rep filename]; +// NSString *combined = [NSString stringWithFormat:@"ScreenShot - %@.png", fileName]; +// NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:combined]; +// +// UIImage *currentImage = [UIImage imageWithCGImage:iref]; +// NSData *currentImageData = UIImagePNGRepresentation(currentImage); +// [currentImageData writeToFile:filePath atomically:YES]; +// + NSLog(@"OK - IMAGE LOADED\n"); + NSDictionary *detectorOptions = @{@"CIDetectorAccuracy": @"CIDetectorAccuracyHigh"}; + CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:detectorOptions]; + CIImage *image = [CIImage imageWithCGImage:iref]; + NSArray *features = [detector featuresInImage:image]; + if (0==features.count) { + NSLog(@"PROBLEM! Feature size is zero!\n"); + callback(@[RCTMakeError(@"Feature size is zero!", nil, nil)]); + return; + } + + CIQRCodeFeature *feature = [features firstObject]; + + NSString *result = feature.messageString; + NSLog(@"result: %@", result); + + if (result) { + callback(@[[NSNull null], result]); + } else { + callback(@[RCTMakeError(@"QR Parse failed!", nil, nil)]); + return; + } + }; + + ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror){ + + //failed to get image. + }; - if (result) { - callback(@[[NSNull null], result]); - } else { - callback(@[RCTMakeError(@"QR Parse failed!", nil, nil)]); - return; - } + ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init]; + NSURL *myAssetUrl = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [assetslibrary assetForURL:myAssetUrl resultBlock:resultblock failureBlock:failureblock]; } + @end