Skip to content

Commit

Permalink
Improve QR Code scan ability (#1036)
Browse files Browse the repository at this point in the history
* qr scan library upgrade + google ML function

* QR scan check bounce

* add white border to QR code image for scanning support

* typescript changes QRGen code

* typescript fixes qrGen

* update test snapshot QRCode

* add test for QRGen

* minor change; review changes

* update QRGen test

* revert google ml function for qr scan

* update QRGen tests

* update QRGen tests

* qrGen minor fix

* merge master

* minor fix QRGen

* remove stale snapshot qrGen

* gradle fix qrGen
  • Loading branch information
rohit-dua authored Oct 2, 2019
1 parent c80f755 commit 5961646
Show file tree
Hide file tree
Showing 12 changed files with 2,186 additions and 23 deletions.
2 changes: 2 additions & 0 deletions packages/mobile/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ android {
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_config_package", "org.celo.mobile"
missingDimensionStrategy 'react-native-camera', 'general'
}
signingConfigs {
release {
Expand Down Expand Up @@ -217,6 +218,7 @@ android {

packagingOptions {
exclude 'META-INF/-no-jdk.kotlin_module'
exclude 'META-INF/androidx.exifinterface_exifinterface.version'
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/mobile/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ allprojects {
flatDir {
dirs celoClientDirectory
}
maven {
url "https://www.jitpack.io"
}
maven {
url "https://maven.google.com"
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"react-native-android-open-settings": "^1.2.0",
"react-native-autocomplete-input": "^3.6.0",
"react-native-bip39": "git://github.com/celo-org/react-native-bip39#1488fa1",
"react-native-camera": "^1.9.1",
"react-native-camera": "2.9.0",
"react-native-clock-sync": "^1.0.0",
"react-native-config": "https://github.com/luggit/react-native-config#89a602b",
"react-native-confirm-device-credentials": "^2.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/mobile/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ declare module 'web3-core-helpers'
declare module '@ungap/url-search-params'
declare module 'react-native-install-referrer'
declare module 'react-native-secure-key-store'
declare module 'qrcode'
2 changes: 1 addition & 1 deletion packages/mobile/src/qrcode/QRCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import variables from '@celo/react-components/styles/variables'
import * as React from 'react'
import { WithNamespaces, withNamespaces } from 'react-i18next'
import { StyleSheet, View } from 'react-native'
import QRCode from 'react-native-qrcode-svg'
import { connect } from 'react-redux'
import { UserContactDetails, userContactDetailsSelector } from 'src/account/reducer'
import { AvatarSelf } from 'src/components/AvatarSelf'
import i18n, { Namespaces } from 'src/i18n'
import { headerWithBackButton } from 'src/navigator/Headers'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import QRCode from 'src/qrcode/QRGen'
import { RootState } from 'src/redux/reducers'
import { shareQRCode, SVG } from 'src/send/actions'
import { currentAccountSelector } from 'src/web3/selectors'
Expand Down
117 changes: 117 additions & 0 deletions packages/mobile/src/qrcode/QRGen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import _QRCode from 'qrcode'
import React, { PureComponent } from 'react'
import Svg, { Path, Rect } from 'react-native-svg'

export function genMatrix(value: any, errorCorrectionLevel: string) {
const arr = Array.prototype.slice.call(
_QRCode.create(value, { errorCorrectionLevel }).modules.data,
0
)
const sqrt = Math.sqrt(arr.length)
return arr.reduce(
(rows, key, index) =>
(index % sqrt === 0 ? rows.push([key]) : rows[rows.length - 1].push(key)) && rows,
[]
)
}

/* calculate the size of the cell and draw the path */
export function calculateMatrix(props: any) {
const { value, size, ecl, onError } = props
try {
const reducedSize = size - 20
const matrix = genMatrix(value, ecl)
const cellSize = reducedSize / matrix.length
return {
cellSize,
path: transformMatrixIntoPath(cellSize, matrix),
}
} catch (error) {
if (onError && typeof onError === 'function') {
onError(error)
} else {
// Pass the error when no handler presented
throw error
}
}
return {}
}

/* project the matrix into path draw */
export function transformMatrixIntoPath(cellSize: number, matrix: any) {
// adjust origin
let d = ''
matrix.forEach((row: any, i: number) => {
let needDraw = false
row.forEach((column: any, j: number) => {
if (column) {
if (!needDraw) {
d += `M${cellSize * j + 10} ${cellSize / 2 + cellSize * i + 10} `
needDraw = true
}
if (needDraw && j === matrix.length - 1) {
d += `L${cellSize * (j + 1) + 10} ${cellSize / 2 + cellSize * i + 10} `
}
} else {
if (needDraw) {
d += `L${cellSize * j + 10} ${cellSize / 2 + cellSize * i + 10} `
needDraw = false
}
}
})
})
return d
}

interface QRProps {
value: string
size: number
color: string
backgroundColor: string
getRef: any
ecl: string
onError: any
}

interface QRState {
cellSize?: number | undefined
path?: string | undefined
}

/**
* A simple component for displaying QR Code using svg
*/
export default class QRCode extends PureComponent<QRProps, QRState> {
static defaultProps = {
value: 'This is a QR Code.',
size: 100,
color: 'black',
backgroundColor: 'white',
ecl: 'M',
onError: undefined,
}

static getDerivedStateFromProps(props: any, state: any) {
// if value has changed, re-calculateMatrix
if (props.value !== state.value || props.size !== state.size) {
return calculateMatrix(props)
}
return null
}

constructor(props: any) {
super(props)
this.state = calculateMatrix(props)
}

render() {
const { getRef, size, color, backgroundColor } = this.props
const { cellSize, path } = this.state
return (
<Svg ref={getRef} width={size} height={size}>
<Rect width={size} height={size} fill={backgroundColor} />
{path && cellSize && <Path d={path} stroke={color} strokeWidth={cellSize} />}
</Svg>
)
}
}
50 changes: 50 additions & 0 deletions packages/mobile/src/qrcode/QRGenTests/QRGen.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react'
import * as renderer from 'react-test-renderer'
import QRCode, { genMatrix } from 'src/qrcode/QRGen'

describe('QRCode', () => {
it('renders correctly', () => {
const tree = renderer.create(<QRCode value="celo" getRef={jest.fn()} />).toJSON()
expect(tree).toMatchSnapshot()
})

// Simulate big data passed to QRCode and check if onError Callback
// Called properly
it('calls onError in case of issue with code generating', () => {
const onErrorMock = jest.fn()
// Rendering with big amount of data that should
// throw an exception
renderer.create(
<QRCode value={new Array(1000000).join('123')} onError={onErrorMock} getRef={jest.fn()} />
)
expect(onErrorMock.mock.calls.length).toBe(2)
})

it('does not call onError in case if value is fine', () => {
const onErrorMock = jest.fn()
renderer.create(<QRCode value="123" onError={onErrorMock} getRef={jest.fn()} />)
expect(onErrorMock.mock.calls.length).toBe(0)
})
})

describe('QRCode Matrix', () => {
it('generates with ecl:M correctly', () => {
const matrix = genMatrix('test', 'M')
expect(matrix).toMatchSnapshot()
})

it('generates with ecl:L correctly', () => {
const matrix = genMatrix('test', 'L')
expect(matrix).toMatchSnapshot()
})

it('generates with ecl:H correctly', () => {
const matrix = genMatrix('test', 'H')
expect(matrix).toMatchSnapshot()
})

it('generates with ecl:Q correctly', () => {
const matrix = genMatrix('test', 'Q')
expect(matrix).toMatchSnapshot()
})
})
Loading

0 comments on commit 5961646

Please sign in to comment.