Merge pull request #6 from mandeepdhiman123/main

Develop to main merge for workflow dispatch to run.
This commit is contained in:
Mandeep Dhiman
2022-02-25 18:32:27 +05:30
committed by GitHub
173 changed files with 58982 additions and 70 deletions

4
.expo-shared/assets.json Normal file
View File

@@ -0,0 +1,4 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug, triage
assignees: jeremi, kneckinator
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement, triage
assignees: jeremi, kneckinator
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,33 @@
name: ID PASS - MOSIP Resident Application Custom build
env:
backendServiceDefaultUrl: https://dev2.mosip.net/residentmobileapp
on:
workflow_dispatch:
inputs:
backendServiceUrl:
description: 'Backend service URL'
required: true
default: 'https://dev2.mosip.net/residentmobileapp'
type: string
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install npm dependencies
run: |
npm install
- name: Build App Newlogic Release
run: |
cd android && ./gradlew :app:assembleNewlogicRelease
env:
BACKEND_SERVICE_URL: ${{ github.event.inputs.backendServiceUrl }}
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: apk-output
path: android/app/build/outputs/apk/newlogic/release/
retention-days: 1

29
.github/workflows/android.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: ID PASS - MOSIP Resident Application
on:
push:
branches:
- main
- develop
tags:
- '*'
pull_request:
branches:
- '*'
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install npm dependencies
run: |
npm install
- name: Build App Newlogic Release
run: |
cd android && ./gradlew :app:assembleNewlogicRelease
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: output
path: android/app/build/outputs/apk/newlogic/release/
retention-days: 1

133
.gitignore vendored
View File

@@ -1,85 +1,80 @@
node_modules/
.expo/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8
# The following patterns were generated by expo-cli
# Built application files
*.apk
*.aar
*.ap_
*.aab
android/app/debug/output-metadata.json
android/app/release/output-metadata.json
# Files for the ART/Dalvik VM
*.dex
# OSX
#
.DS_Store
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Local configuration file (sdk path, etc)
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
*.hprof
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Bundle artifacts
*.jsbundle
# Freeline
freeline.py
freeline/
freeline_project_description.json
# CocoaPods
/ios/Pods/
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Expo
.expo/
web-build/
dist/
# Version control
vcs.xml
# @end expo-cli
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
.vscode/
temp/

13
.prettierrc.json Normal file
View File

@@ -0,0 +1,13 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"bracketSameLine": true,
"jsxSingleQuote": false,
"quoteProps": "consistent",
"printWidth": 80,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}

24
App.tsx Normal file
View File

@@ -0,0 +1,24 @@
import React, { useContext } from 'react';
import AppLoading from 'expo-app-loading';
import { AppLayout } from './screens/AppLayout';
import { useFont } from './shared/hooks/useFont';
import { GlobalContextProvider } from './components/GlobalContextProvider';
import { GlobalContext } from './shared/GlobalContext';
import { useSelector } from '@xstate/react';
import { selectIsReady } from './machines/app';
const AppInitialization: React.FC = (props) => {
const { appService } = useContext(GlobalContext);
const hasFontsLoaded = useFont();
const isReady = useSelector(appService, selectIsReady);
return isReady && hasFontsLoaded ? <AppLayout /> : <AppLoading />;
};
export default function App() {
return (
<GlobalContextProvider>
<AppInitialization />
</GlobalContextProvider>
);
}

13
Credits.md Normal file
View File

@@ -0,0 +1,13 @@
# Credits
This project is made possible by the people and organizations listed in this document.
## Contributors
<img src="docs/images/id_pass_logo.svg" width="150" height="50">
[ID PASS](https://www.idpass.org/)
<img src="docs/images/newlogic_logo.svg" width="100" height="200">
[Newlogic](https://www.newlogic.com/)

View File

@@ -1,2 +1,51 @@
# inji
This is a mobile id client.
MOSIP citizen app.
## Dependencies
Be sure to have the following build tools installed before proceeding:
- [Gradle](https://gradle.org/install/)
- [Java 8](https://www.oracle.com/ph/java/technologies/javase/javase8-archive-downloads.html)
- [Expo](https://docs.expo.dev/get-started/installation/)
## Running the app
```bash
# Install all dependencies
npm install
# run dev client
npm start
# run Inji directly to connected emulator or device (Default)
npm run android:newlogic
# run Inji Philippines directly to connected emulator or device
npm run android:ph
```
# Building from Source
## Build via Android Studio
The app is available in this repository's `frontend/android` directory. Open this directory in Android Studio (version 4.1 and above) and the app can be built and run from there.
More info here: [Build your app using Android Studio](https://developer.android.com/studio/run)
## Build via command line
1. Build for Mosip Philippines test
```bash
npm run build:android:ph
```
2. Build for Newlogic test
```bash
npm run build:android:newlogic
```
Note for release builds you will need to have a keystore: [Create a Keystore](https://medium.com/@tom.truyen/create-an-android-keystore-using-keytool-commandline-10399a62e774)
More info here: [Build your app from the command line](https://developer.android.com/studio/build/building-cmdline)
## Credits
Credits listed [here](/Credits.md)

28
android/.project Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MOSIP Resident App</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1637742618855</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@@ -0,0 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=C\:/Program Files/OpenJDK/openjdk-11.0.12_7
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

55
android/app/BUCK Normal file
View File

@@ -0,0 +1,55 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "io.mosip.residentapp",
)
android_resource(
name = "res",
package = "io.mosip.residentapp",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

287
android/app/build.gradle Normal file
View File

@@ -0,0 +1,287 @@
plugins {
id 'com.gladed.androidgitversion' version '0.4.14'
}
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
bundleInDebug: true,
bundleInRelease: true,
devDisabledInRelease: true,
cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
]
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
androidGitVersion {
baseCode 1
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
ext {
APP_NAME_RELEASE = "@string/app_name"
APP_NAME_PH = "@string/app_name_ph"
APP_NAME_NEWLOGIC = "@string/app_name_newlogic"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId 'io.mosip.residentapp'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
// Update versionName and/or versionCode via git tag <XX.xx.xx>
// More info here:
// https://github.com/gladed/gradle-android-git-version#3-use-a-git-tag-to-specify-your-version-number-see-semantic-versioning
versionName androidGitVersion.name()
versionCode androidGitVersion.code()
manifestPlaceholders = [
APP_NAME : APP_NAME_RELEASE
]
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
release {
// TODO add proper release keystore via local.properties
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
flavorDimensions "mosip"
productFlavors {
ph {
versionName defaultConfig.versionName + "-ph"
buildConfigField "boolean", "ENABLE_LOG", "true"
manifestPlaceholders = [
APP_NAME: APP_NAME_PH
]
dimension "mosip"
}
newlogic {
buildConfigField "boolean", "ENABLE_LOG", "false"
versionName defaultConfig.versionName + "-newlogic"
manifestPlaceholders = [
APP_NAME: APP_NAME_NEWLOGIC
]
dimension "mosip"
}
}
android.applicationVariants.all { variant ->
variant.outputs.all {
def datetime = new Date().format('yyyyMMdd_HHmm')
outputFileName = "${defaultConfig.applicationId}-${variant.versionName}_${datetime}.apk"
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
// All fresco packages should use the same version
if (isGifEnabled || isWebpEnabled) {
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.0.0'
}
if (isGifEnabled) {
// For animated gif support
implementation 'com.facebook.fresco:animated-gif:2.0.0'
}
if (isWebpEnabled) {
// For webp support
implementation 'com.facebook.fresco:webpsupport:2.0.0'
if (isWebpAnimatedEnabled) {
// Animated webp support
implementation 'com.facebook.fresco:animated-webp:2.0.0'
}
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
debugImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-debug.aar"))
releaseImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-release.aar"))
} else {
implementation jscFlavor
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesAppBuildGradle(project)
apply from: "./eas-build.gradle"

View File

@@ -0,0 +1,19 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

BIN
android/app/debug.keystore Normal file

Binary file not shown.

View File

@@ -0,0 +1,74 @@
// Build integration with EAS
import java.nio.file.Paths
android {
signingConfigs {
release {
// This is necessary to avoid needing the user to define a release signing config manually
// If no release config is defined, and this is not present, build for assembleRelease will crash
}
}
buildTypes {
release {
// This is necessary to avoid needing the user to define a release build type manually
}
}
}
def isEasBuildConfigured = false
tasks.whenTaskAdded {
def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }
if (debug) {
return
}
// We only need to configure EAS build once
if (isEasBuildConfigured) {
return
}
isEasBuildConfigured = true;
android.signingConfigs.release {
def credentialsJson = rootProject.file("../credentials.json");
if (credentialsJson.exists()) {
if (storeFile && !System.getenv("EAS_BUILD")) {
println("Path to release keystore file is already set, ignoring 'credentials.json'")
} else {
try {
def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)
def keystorePath = Paths.get(credentials.android.keystore.keystorePath);
def storeFilePath = keystorePath.isAbsolute()
? keystorePath
: rootProject.file("..").toPath().resolve(keystorePath);
storeFile storeFilePath.toFile()
storePassword credentials.android.keystore.keystorePassword
keyAlias credentials.android.keystore.keyAlias
if (credentials.android.keystore.containsKey("keyPassword")) {
keyPassword credentials.android.keystore.keyPassword
} else {
// key password is required by Gradle, but PKCS keystores don't have one
// using the keystore password seems to satisfy the requirement
keyPassword credentials.android.keystore.keystorePassword
}
} catch (Exception e) {
println("An error occurred while parsing 'credentials.json': " + e.message)
}
}
} else {
if (storeFile == null) {
println("Couldn't find a 'credentials.json' file, skipping release keystore configuration")
}
}
}
android.buildTypes.release {
signingConfig android.signingConfigs.release
}
}

10
android/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,10 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
</manifest>

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package io.mosip.residentapp;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}

View File

@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.mosip.residentapp">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"/>
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="43.0.0"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@nlpaolo/mosip-resident-app"/>
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustPan" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="io.mosip.residentapp"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -0,0 +1,127 @@
package io.mosip.residentapp;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import expo.modules.ReactActivityDelegateWrapper;
/**
* IMPORTANT NOTE: The Android permission flow here works
* for Android 10 and below, and Android 11,
* and under continuous investigation if other manufacturers
* fails to work, etc.
*/
public class MainActivity extends ReactActivity {
private static final String[] REQUIRED_PERMISSIONS = new String[] {
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
};
private static final int REQUEST_CODE_REQUIRED_PERMISSIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
super.onCreate(null);
}
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "main";
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!hasPermissions(this, REQUIRED_PERMISSIONS)) {
this.requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_REQUIRED_PERMISSIONS);
}
}
// TODO Commenting this only for now if permission is not working for other Android 11 manifacturer/devices
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
// if (wifi != null){
// WifiManager.MulticastLock lock = wifi.createMulticastLock("IdpassSmartshareExample");
// lock.acquire();
// }
// }
// Must add this to onDestroy/onStop or disconnect to save battery
// lock.release();
}
/**
* Returns true if the app was granted all the permissions. Otherwise, returns false.
*/
private static boolean hasPermissions(Context context, String... permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* Handles user acceptance (or denial) of our permission request.
*/
@CallSuper
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode != REQUEST_CODE_REQUIRED_PERMISSIONS) {
return;
}
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
// Toast.makeText(this, R.string.error_missing_permissions, Toast.LENGTH_LONG).show();
// connectButton.setEnabled(false);
Log.d("Main", "Denied");
return;
}
}
recreate();
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegateWrapper(
this,
new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
}
);
}
}

View File

@@ -0,0 +1,42 @@
package io.mosip.residentapp;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import expo.modules.ReactActivityDelegateWrapper;
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
super.onCreate(null);
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "main";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegateWrapper(
this,
new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
});
}
}

View File

@@ -0,0 +1,103 @@
package io.mosip.residentapp;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
this,
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage();
}
});
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("io.mosip.residentapp.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/splashscreen_background"/>
</layer-list>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_mosip"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_mosip"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources/>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<color name="splashscreen_background">#ffffff</color>
<color name="iconBackground">#FFFFFF</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color>
</resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<string name="app_name">MOSIP Resident App</string>
<string name="app_name_ph">MOSIP Resident App - PH</string>
<string name="app_name_newlogic">MOSIP Resident App - Newlogic</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
</resources>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColor">@android:color/black</item>
<item name="android:editTextStyle">@style/ResetEditText</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
</style>
<style name="ResetEditText" parent="@android:style/Widget.EditText">
<item name="android:padding">0dp</item>
<item name="android:textColorHint">#c8c8c8</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<item name="android:windowBackground">@drawable/splashscreen</item>
</style>
</resources>

42
android/build.gradle Normal file
View File

@@ -0,0 +1,42 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "29.0.3"
minSdkVersion = 21
compileSdkVersion = 30
targetSdkVersion = 30
}
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:4.1.0")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../android"))
}
maven {
// Android JSC is installed from npm
url(new File(["node", "--print", "require.resolve('jsc-android/package.json')"].execute(null, rootDir).text.trim(), "../dist"))
}
google()
mavenCentral()
jcenter()
maven { url 'https://www.jitpack.io' }
}
}
allprojects { repositories { maven { url "$rootDir/../node_modules/expo-camera/android/maven" } } }

41
android/gradle.properties Normal file
View File

@@ -0,0 +1,41 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.54.0
# The hosted JavaScript engine
# Supported values: expo.jsEngine = "hermes" | "jsc"
expo.jsEngine=jsc
# Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=true
# Enable webp support in React Native images (~85 KB increase)
expo.webp.enabled=true
# Enable animated webp support (~3.4 MB increase)
# Disabled by default because iOS doesn't support animated webp
expo.webp.animated=false

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

183
android/gradlew vendored Executable file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

103
android/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,103 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

9
android/settings.gradle Normal file
View File

@@ -0,0 +1,9 @@
rootProject.name = 'MOSIP Resident App'
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
useExpoModules()
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesSettingsGradle(settings)
include ':app'

35
app.config.ts Normal file
View File

@@ -0,0 +1,35 @@
export default {
name: 'MOSIP Resident App',
slug: 'mosip-resident-app',
version: '1.0.0',
orientation: 'portrait',
icon: './assets/icon.png',
splash: {
image: './assets/splash.png',
resizeMode: 'contain',
backgroundColor: '#ffffff',
},
updates: {
fallbackToCacheTimeout: 0,
},
assetBundlePatterns: ['**/*'],
ios: {
bundleIdentifier: 'io.mosip.residentapp',
buildNumber: '1.0.0',
supportsTablet: true,
},
android: {
package: 'io.mosip.residentapp',
versionCode: 1,
adaptiveIcon: {
foregroundImage: './assets/adaptive-icon.png',
backgroundColor: '#FFFFFF',
},
},
web: {
favicon: './assets/favicon.png',
},
extra: {
backendServiceUrl: process.env.BACKEND_SERVICE_URL,
},
};

BIN
assets/adaptive-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/idpass-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/mosip-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

6
babel.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { TextItem } from './ui/TextItem';
export const DeviceInfoList: React.FC<DeviceInfoProps> = (props) => {
return (
<React.Fragment>
<TextItem
divider
label={props.of === 'receiver' ? 'Requested by' : 'Sent by'}
text={props.deviceInfo.deviceName}
/>
<TextItem divider label="Name" text={props.deviceInfo.name} />
<TextItem
divider
label="Device reference number"
text={props.deviceInfo.deviceId}
/>
</React.Fragment>
);
};
interface DeviceInfoProps {
of: 'sender' | 'receiver';
deviceInfo: DeviceInfo;
}
export interface DeviceInfo {
deviceName: string;
name: string;
deviceId: string;
}

View File

@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import { Dimensions } from 'react-native';
import { ListItem, Overlay, Input } from 'react-native-elements';
import { Text, Column, Row, Button } from './ui';
import { Colors } from './ui/styleUtils';
export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
const [isEditing, setIsEditing] = useState(false);
const [newValue, setNewValue] = useState(props.value);
return (
<ListItem bottomDivider onPress={() => setIsEditing(true)}>
<ListItem.Content>
<ListItem.Title>
<Text>{props.label}</Text>
</ListItem.Title>
</ListItem.Content>
<Text color={Colors.Grey}>{props.value}</Text>
<Overlay
overlayStyle={{ padding: 24, elevation: 6 }}
isVisible={isEditing}
onBackdropPress={dismiss}>
<Column width={Dimensions.get('screen').width * 0.8}>
<Text>Edit {props.label}</Text>
<Input autoFocus value={newValue} onChangeText={setNewValue} />
<Row>
<Button
fill
type="clear"
title="Cancel"
onPress={() => setIsEditing(false)}
/>
<Button fill title="Save" onPress={edit} />
</Row>
</Column>
</Overlay>
</ListItem>
);
function edit() {
props.onEdit(newValue);
dismiss();
}
function dismiss() {
setNewValue('');
setIsEditing(false);
}
};
interface EditableListItemProps {
label: string;
value: string;
onEdit: (newValue: string) => void;
}

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { useInterpret } from '@xstate/react';
import { appMachine, logState } from '../machines/app';
import { GlobalContext } from '../shared/GlobalContext';
export const GlobalContextProvider: React.FC = (props) => {
const appService = useInterpret(appMachine);
// TODO: remove in production builds
appService.subscribe(logState);
return (
<GlobalContext.Provider value={{ appService }}>
{props.children}
</GlobalContext.Provider>
);
};

View File

@@ -0,0 +1,26 @@
import React, { useState } from 'react';
import { View } from 'react-native';
// import { Picker } from '@react-native-community/picker';
// import { ItemValue } from '@react-native-community/picker/typings/Picker';
import { Icon } from 'react-native-elements';
import { Colors } from './ui/styleUtils';
const DEFAULT_LANGUAGE = 'en';
export function LanguageSelector() {
// const [language, setLanguage] = useState<ItemValue>(DEFAULT_LANGUAGE);
return (
<View>
<Icon name="language" color={Colors.Orange} />
{/* <Picker
mode="dropdown"
selectedValue={language}
style={{ height: 50, width: 150 }}
onValueChange={(itemValue: ItemValue) => setLanguage(itemValue)}>
<Picker.Item label="English" value="en" />
<Picker.Item label="Tagalog" value="tl" />
</Picker> */}
</View>
);
}

18
components/Logo.tsx Normal file
View File

@@ -0,0 +1,18 @@
import React from 'react';
import { View, Image } from 'react-native';
export const Logo: React.FC<LogoProps> = (props) => {
return (
<View>
<Image
style={{ resizeMode: 'contain', ...props }}
source={require('../assets/mosip-logo.png')}
/>
</View>
);
};
interface LogoProps {
width?: number | string;
height?: number | string;
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import { Overlay, LinearProgress } from 'react-native-elements';
import { Column, Text } from './ui';
import { Colors, elevation } from './ui/styleUtils';
const styles = StyleSheet.create({
overlay: {
...elevation(5),
backgroundColor: Colors.White,
},
});
export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
return (
<Overlay
isVisible={props.isVisible}
overlayStyle={styles.overlay}
onBackdropPress={props.onBackdropPress}
>
<Column padding="24" width={Dimensions.get('screen').width * 0.8}>
{props.title && (
<Text weight="semibold" margin="0 0 12 0">
{props.title}
</Text>
)}
{props.message && <Text margin="0 0 12 0">{props.message}</Text>}
{props.hasProgress && (
<LinearProgress variant="indeterminate" color={Colors.Orange} />
)}
</Column>
</Overlay>
);
};
interface MessageOverlayProps {
isVisible: boolean;
title?: string;
message?: string;
hasProgress?: boolean;
onBackdropPress?: () => void;
}

View File

@@ -0,0 +1,30 @@
import React, { useEffect, useState } from 'react';
import { PinInput } from './PinInput';
export const MAX_PIN = 6;
export const PasscodeVerify: React.FC<PasscodeVerifyProps> = (props) => {
const [isVerified, setIsVerified] = useState(false);
useEffect(() => {
if (isVerified) {
props.onSuccess();
}
}, [isVerified]);
return <PinInput length={MAX_PIN} onDone={verify} />;
function verify(value: string) {
if (props.passcode === value) {
setIsVerified(true);
} else {
props.onError('Passcode did not match.');
}
}
};
interface PasscodeVerifyProps {
passcode: string;
onSuccess: () => void;
onError?: (error: string) => void;
}

61
components/PinInput.tsx Normal file
View File

@@ -0,0 +1,61 @@
import React, { useEffect } from 'react';
import { StyleSheet, TextInput } from 'react-native';
import { usePinInput } from '../machines/pinInput';
import { Row } from './ui';
import { Colors } from './ui/styleUtils';
const styles = StyleSheet.create({
input: {
borderBottomWidth: 1,
borderColor: Colors.Grey,
color: Colors.Black,
flex: 1,
fontFamily: 'Poppins_600SemiBold',
fontSize: 18,
fontWeight: '600',
height: 40,
lineHeight: 28,
margin: 8,
textAlign: 'center',
},
});
export const PinInput: React.FC<PinInputProps> = (props) => {
const { state, send, events } = usePinInput(props.length);
const { inputRefs, values } = state.context;
const { UPDATE_INPUT, FOCUS_INPUT, KEY_PRESS } = events;
useEffect(() => {
if (props.onDone && values.filter(Boolean).length === inputRefs.length) {
props.onDone(values.join(''));
}
}, [state]);
return (
<Row width="100%">
{inputRefs.map((input, index) => (
<TextInput
selectTextOnFocus
keyboardType="numeric"
maxLength={1}
selectionColor={Colors.Orange}
style={styles.input}
key={index}
ref={input}
value={values[index]}
// KNOWN ISSUE: https://github.com/facebook/react-native/issues/19507
onKeyPress={({ nativeEvent }) => send(KEY_PRESS(nativeEvent.key))}
onChangeText={(value: string) =>
send(UPDATE_INPUT(value.replace(/[^0-9]/g, ''), index))
}
onFocus={() => send(FOCUS_INPUT(index))}
/>
))}
</Row>
);
};
interface PinInputProps {
length: number;
onDone?: (value: string) => void;
}

128
components/QrScanner.tsx Normal file
View File

@@ -0,0 +1,128 @@
import React, { useContext, useEffect, useState } from 'react';
import { Camera } from 'expo-camera';
import { BarCodeEvent, BarCodeScanner } from 'expo-barcode-scanner';
import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Colors } from './ui/styleUtils';
import { Button, Text } from './ui';
import { GlobalContext } from '../shared/GlobalContext';
import { useSelector } from '@xstate/react';
import { selectIsActive } from '../machines/app';
const styles = StyleSheet.create({
scannerContainer: {
borderWidth: 4,
borderColor: Colors.Black,
borderRadius: 32,
justifyContent: 'center',
height: 300,
width: 300,
overflow: 'hidden',
},
scanner: {
height: 400,
width: '100%',
margin: 'auto'
},
buttonContainer: {
height: '100%',
width: '100%',
},
flipButtonContainer: {
position: 'absolute',
width: '80%',
top: '110%',
},
buttonStyle: {
position: 'absolute',
width: '100%',
bottom: -90,
},
button: {
alignSelf: 'center',
alignItems: 'center',
},
});
export const QrScanner: React.FC<QrScannerProps> = (props) => {
const { appService } = useContext(GlobalContext);
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [type, setType] = useState(Camera.Constants.Type.back);
const isActive = useSelector(appService, selectIsActive);
const openSettings = () => {
Linking.openSettings();
};
useEffect(() => {
(async () => {
const response = await Camera.requestCameraPermissionsAsync();
setHasPermission(response.granted);
})();
}, []);
useEffect(() => {
if (isActive && hasPermission === false) {
(async () => {
const response = await Camera.requestCameraPermissionsAsync();
setHasPermission(response.granted);
})();
}
}, [isActive]);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return (
<View style={styles.buttonContainer}>
<Text align="center">
This app uses the camera to scan the QR code of another device.
</Text>
<View style={styles.buttonStyle}>
<Button title="Allow access to camera" onPress={openSettings} />
</View>
</View>
);
}
return (
<View>
<View style={styles.scannerContainer}>
<Camera
style={styles.scanner}
barCodeScannerSettings={{
barcodeTypes: [BarCodeScanner.Constants.BarCodeType.qr]
}}
onBarCodeScanned={scanned ? undefined : onBarcodeScanned}
type={type}
/>
</View>
<View style={styles.flipButtonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Icon name="flip-camera-ios" color={Colors.Black} size={64} />
</TouchableOpacity>
</View>
</View>
);
function onBarcodeScanned(event: BarCodeEvent) {
props.onQrFound(event.data);
setScanned(true);
}
};
interface QrScannerProps {
onQrFound: (data: string) => void;
}

View File

@@ -0,0 +1,39 @@
import React, { useEffect, useRef } from 'react';
import { Animated, Easing } from 'react-native';
import { Icon } from 'react-native-elements';
export const RotatingIcon: React.FC<RotatingIconProps> = (props) => {
const rotationValue = useRef(
new Animated.Value(props.clockwise ? 0 : 1)
).current;
const rotation = rotationValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
useEffect(() => {
Animated.loop(
Animated.timing(rotationValue, {
toValue: props.clockwise ? 1 : 0,
duration: props.duration || 3000,
easing: Easing.linear,
useNativeDriver: true,
})
).start();
}, []);
return (
<Animated.View style={{ transform: [{ rotate: rotation }] }}>
<Icon name={props.name} size={props.size} color={props.color} />
</Animated.View>
);
};
interface RotatingIconProps {
name: string;
size?: number;
duration?: number;
clockwise?: boolean;
color?: string;
}

View File

@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import { Overlay, Input } from 'react-native-elements';
import { Button, Column, Row, Text } from './ui';
import { Colors, elevation } from './ui/styleUtils';
const styles = StyleSheet.create({
overlay: {
...elevation(5),
backgroundColor: Colors.White,
padding: 0,
},
});
export const TextEditOverlay: React.FC<EditOverlayProps> = (props) => {
const [value, setValue] = useState(props.value);
return (
<Overlay
isVisible={props.isVisible}
overlayStyle={styles.overlay}
onBackdropPress={props.onDismiss}>
<Column padding="24" width={Dimensions.get('screen').width * 0.8}>
<Text weight="semibold" margin="0 0 16 0">
{props.label}
</Text>
<Input autoFocus value={value} onChangeText={setValue} />
<Row>
<Button
fill
type="clear"
title="Cancel"
onPress={dismiss}
margin="0 8 0 0"
/>
<Button fill title="Save" onPress={() => props.onSave(value)} />
</Row>
</Column>
</Overlay>
);
function dismiss() {
setValue(props.value);
props.onDismiss();
}
};
interface EditOverlayProps {
isVisible: boolean;
label: string;
value: string;
onSave: (value: string) => void;
onDismiss: () => void;
}

143
components/VidDetails.tsx Normal file
View File

@@ -0,0 +1,143 @@
import React from 'react';
import { Image } from 'react-native';
import { ListItem } from 'react-native-elements';
import { VID, VIDCredential } from '../types/vid';
import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
export const VidDetails: React.FC<VidDetailsProps> = (props) => {
return (
<Column>
<Row padding="16 24">
<Column fill elevation={1} padding="12 16" margin="0 16 0 0">
<Text size="smaller" color={Colors.Grey}>
Generated
</Text>
<Text weight="bold" size="smaller">
{new Date(props.vid?.generatedOn).toLocaleDateString()}
</Text>
</Column>
<Column fill elevation={1} padding="12 16" margin="0 16 0 0">
<Text size="smaller" color={Colors.Grey}>
UIN
</Text>
<Text weight="bold" size="smaller">
{props.vid?.uin}
</Text>
</Column>
<Column fill elevation={1} padding="12 16" margin="">
<Text size="smaller" color={Colors.Grey}>
Status
</Text>
<Text weight="bold" size="smaller">
Valid
</Text>
</Column>
</Row>
{props.vid?.credential.biometrics && (
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Photo</ListItem.Subtitle>
<ListItem.Content>
<Image
source={{ uri: props.vid?.credential.biometrics.face }}
style={{
width: 110,
height: 110,
resizeMode: 'cover',
marginTop: 8,
}}
/>
</ListItem.Content>
</ListItem.Content>
</ListItem>
)}
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Full name</ListItem.Subtitle>
<ListItem.Title>{props.vid?.credential.fullName}</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Gender</ListItem.Subtitle>
<ListItem.Title>
{getLocalizedField(props.vid?.credential.gender)}
</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Date of birth</ListItem.Subtitle>
<ListItem.Title>{props.vid?.credential.dateOfBirth}</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Phone number</ListItem.Subtitle>
<ListItem.Title>{props.vid?.credential.phone}</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Email</ListItem.Subtitle>
<ListItem.Title>{props.vid?.credential.email}</ListItem.Title>
</ListItem.Content>
</ListItem>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Address</ListItem.Subtitle>
<ListItem.Title>
{getFullAddress(props.vid?.credential)}
</ListItem.Title>
</ListItem.Content>
</ListItem>
{Boolean(props.vid?.reason) && (
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Reason for sharing</ListItem.Subtitle>
<ListItem.Title>{props.vid?.reason}</ListItem.Title>
</ListItem.Content>
</ListItem>
)}
</Column>
);
};
interface VidDetailsProps {
vid: VID;
}
interface LocalizedField {
language: string;
value: string;
}
function getFullAddress(credential: VIDCredential) {
if (!credential) {
return '';
}
const fields = [
'addressLine1',
'addressLine2',
'addressLine3',
'city',
'province',
];
return (
fields.map((field) => getLocalizedField(credential[field])).join(', ') +
', ' +
credential.postalCode
);
}
function getLocalizedField(rawField: string) {
try {
const locales: LocalizedField[] = JSON.parse(rawField);
// TODO: language switching
return locales.find((locale) => locale.language === 'eng').value;
} catch (e) {
return '';
}
}

107
components/VidItem.tsx Normal file
View File

@@ -0,0 +1,107 @@
import React, { useContext, useRef } from 'react';
import { useInterpret, useSelector } from '@xstate/react';
import { Pressable, StyleSheet } from 'react-native';
import { CheckBox, Icon } from 'react-native-elements';
import { ActorRefFrom } from 'xstate';
import {
createVidItemMachine,
selectCredential,
selectGeneratedOn,
selectTag,
selectUin,
vidItemMachine,
} from '../machines/vidItem';
import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
import { GlobalContext } from '../shared/GlobalContext';
const styles = StyleSheet.create({
title: {
color: Colors.Black,
backgroundColor: 'transparent',
},
loadingTitle: {
color: 'transparent',
backgroundColor: Colors.Grey5,
borderRadius: 4,
},
subtitle: {
backgroundColor: 'transparent',
},
loadingSubtitle: {
backgroundColor: Colors.Grey,
borderRadius: 4,
},
container: {
backgroundColor: Colors.White,
},
loadingContainer: {
backgroundColor: Colors.Grey6,
borderRadius: 4,
},
});
export const VidItem: React.FC<VidItemProps> = (props) => {
const { appService } = useContext(GlobalContext);
const machine = useRef(
createVidItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vidKey
)
);
const service = useInterpret(machine.current);
const uin = useSelector(service, selectUin);
const tag = useSelector(service, selectTag);
const credential = useSelector(service, selectCredential);
const generatedOn = useSelector(service, selectGeneratedOn);
return (
<Pressable onPress={() => props.onPress(service)} disabled={!credential}>
<Row
elevation={!credential ? 0 : 2}
crossAlign="center"
margin={props.margin}
backgroundColor={!credential ? Colors.Grey6 : Colors.White}
padding="16 24"
style={!credential ? styles.loadingContainer : styles.container}>
<Column fill margin="0 24 0 0">
<Text
weight="semibold"
style={!credential ? styles.loadingTitle : styles.title}
margin="0 0 6 0">
{!credential ? '' : tag || uin}
</Text>
<Text
size="smaller"
numLines={1}
style={!credential ? styles.loadingSubtitle : styles.subtitle}>
{!credential ? '' : credential.fullName + ' · ' + generatedOn}
</Text>
</Column>
{credential ? (
props.selectable ? (
<CheckBox
checked={props.selected}
checkedIcon={<Icon name="radio-button-checked" />}
uncheckedIcon={<Icon name="radio-button-unchecked" />}
onPress={() => props.onPress(service)}
/>
) : (
<Icon name="chevron-right" />
)
) : (
<RotatingIcon name="sync" color={Colors.Grey5} />
)}
</Row>
</Pressable>
);
};
interface VidItemProps {
vidKey: string;
margin?: string;
selectable?: boolean;
selected?: boolean;
onPress?: (vidRef?: ActorRefFrom<typeof vidItemMachine>) => void;
}

84
components/ui/Button.tsx Normal file
View File

@@ -0,0 +1,84 @@
import React from 'react';
import {
Button as RNEButton,
ButtonProps as RNEButtonProps,
} from 'react-native-elements';
import {
GestureResponderEvent,
StyleProp,
StyleSheet,
ViewStyle,
} from 'react-native';
import { Text } from './Text';
import { Colors, spacing } from './styleUtils';
const styles = StyleSheet.create({
fill: {
flex: 1,
},
solid: {
backgroundColor: Colors.Orange,
},
clear: {
backgroundColor: 'transparent',
},
outline: {
backgroundColor: 'transparent',
borderColor: Colors.Orange,
},
container: {
height: 48,
flexDirection: 'row',
},
disabled: {
opacity: 0.5,
},
});
export const Button: React.FC<ButtonProps> = (props) => {
const type = props.type || 'solid';
const buttonStyle: StyleProp<ViewStyle> = [styles.fill, styles[type]];
const containerStyle: StyleProp<ViewStyle> = [
styles.container,
props.disabled ? styles.disabled : null,
props.margin ? spacing('margin', props.margin) : null,
];
const handleOnPress = (event: GestureResponderEvent) => {
if (!props.disabled && props.onPress) {
props.onPress(event);
}
};
return (
<RNEButton
buttonStyle={buttonStyle}
containerStyle={[props.fill ? styles.fill : null, containerStyle]}
type={props.type}
raised={props.raised}
title={
<Text
weight="semibold"
color={type === 'solid' ? Colors.White : Colors.Orange}>
{props.title}
</Text>
}
icon={props.icon}
onPress={handleOnPress}
loading={props.loading}
/>
);
};
interface ButtonProps {
title: string;
disabled?: boolean;
margin?: string;
type?: RNEButtonProps['type'];
onPress?: RNEButtonProps['onPress'];
fill?: boolean;
raised?: boolean;
loading?: boolean;
icon?: RNEButtonProps['icon'];
}

77
components/ui/Layout.tsx Normal file
View File

@@ -0,0 +1,77 @@
import React from 'react';
import {
FlexStyle,
StyleProp,
SafeAreaView,
ViewStyle,
StyleSheet,
ScrollView,
RefreshControlProps,
} from 'react-native';
import { elevation, ElevationLevel, spacing } from './styleUtils';
function createLayout(
direction: FlexStyle['flexDirection'],
mainAlign?: FlexStyle['justifyContent'],
crossAlign?: FlexStyle['alignItems']
) {
const layoutStyles = StyleSheet.create({
base: {
flexDirection: direction,
justifyContent: mainAlign,
alignItems: crossAlign,
},
fill: {
flex: 1,
},
});
const Layout: React.FC<LayoutProps> = (props) => {
const styles: StyleProp<ViewStyle> = [
layoutStyles.base,
props.fill ? layoutStyles.fill : null,
props.padding ? spacing('padding', props.padding) : null,
props.margin ? spacing('margin', props.margin) : null,
props.backgroundColor ? { backgroundColor: props.backgroundColor } : null,
props.width ? { width: props.width } : null,
props.height ? { height: props.height } : null,
props.align ? { justifyContent: props.align } : null,
props.crossAlign ? { alignItems: props.crossAlign } : null,
props.elevation ? elevation(props.elevation) : null,
props.style ? props.style : null,
];
return props.scroll ? (
<ScrollView
contentContainerStyle={styles}
refreshControl={props.refreshControl}>
{props.children}
</ScrollView>
) : (
<SafeAreaView style={styles}>{props.children}</SafeAreaView>
);
};
return Layout;
}
export const Row = createLayout('row');
export const Column = createLayout('column');
export const Centered = createLayout('column', 'center', 'center');
interface LayoutProps {
fill?: boolean;
align?: FlexStyle['justifyContent'];
crossAlign?: FlexStyle['alignItems'];
padding?: string;
margin?: string;
backgroundColor?: string;
width?: number | string;
height?: number | string;
elevation?: ElevationLevel;
scroll?: boolean;
refreshControl?: React.ReactElement<RefreshControlProps>;
style?: StyleProp<ViewStyle>;
}

53
components/ui/Modal.tsx Normal file
View File

@@ -0,0 +1,53 @@
import React from 'react';
import { Dimensions, Modal as RNModal, StyleSheet } from 'react-native';
import { Icon } from 'react-native-elements';
import { Column, Row, Text } from '.';
import { Colors, ElevationLevel } from './styleUtils';
const styles = StyleSheet.create({
modal: {
width: Dimensions.get('screen').width,
height: Dimensions.get('screen').height,
},
});
export const Modal: React.FC<ModalProps> = (props) => {
return (
<RNModal
animationType="slide"
style={styles.modal}
visible={props.isVisible}
onRequestClose={props.onDismiss}>
<Column fill>
<Row padding="16 32" elevation={props.headerElevation}>
{props.headerRight ? (
<Icon
name="chevron-left"
onPress={props.onDismiss}
color={Colors.Orange}
/>
) : null}
<Row fill align="center">
<Text weight="semibold">{props.headerTitle}</Text>
</Row>
{props.headerRight || (
<Icon
name="close"
onPress={props.onDismiss}
color={Colors.Orange}
/>
)}
</Row>
{props.children}
</Column>
</RNModal>
);
};
export interface ModalProps {
isVisible: boolean;
onDismiss: () => void;
headerTitle?: string;
headerElevation?: ElevationLevel;
headerRight?: React.ReactElement;
}

60
components/ui/Text.tsx Normal file
View File

@@ -0,0 +1,60 @@
import React from 'react';
import { StyleProp, TextStyle, StyleSheet, Text as RNText } from 'react-native';
import { Colors, spacing } from './styleUtils';
const styles = StyleSheet.create({
base: {
color: Colors.Black,
fontSize: 18,
lineHeight: 28,
},
regular: {
fontFamily: 'Poppins_400Regular',
},
semibold: {
fontFamily: 'Poppins_600SemiBold',
},
bold: {
fontFamily: 'Poppins_700Bold',
},
small: {
fontSize: 14,
lineHeight: 21,
},
smaller: {
fontSize: 12,
lineHeight: 18,
},
});
export const Text: React.FC<TextProps> = (props: TextProps) => {
const weight = props.weight || 'regular';
const textStyles: StyleProp<TextStyle> = [
styles.base,
styles[weight],
props.color ? { color: props.color } : null,
props.align ? { textAlign: props.align } : null,
props.margin ? spacing('margin', props.margin) : null,
props.size ? styles[props.size] : null,
props.style ? props.style : null,
];
return (
<RNText style={textStyles} numberOfLines={props.numLines}>
{props.children}
</RNText>
);
};
interface TextProps {
children: React.ReactNode;
color?: string;
weight?: 'regular' | 'semibold' | 'bold';
align?: TextStyle['textAlign'];
margin?: string;
size?: 'small' | 'smaller' | 'regular';
lineHeight?: number;
numLines?: number;
style?: StyleProp<TextStyle>;
}

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { Column, Text } from '.';
import { Colors } from './styleUtils';
export const TextItem: React.FC<TextItemProps> = (props) => {
return (
<Column
backgroundColor={Colors.White}
margin={props.margin}
padding={props.label ? '16 24' : '12 24'}
style={{
borderBottomColor: Colors.Grey6,
borderBottomWidth: props.divider ? 1 : 0,
}}>
{props.label && (
<Text size="smaller" color={Colors.Grey} weight="semibold">
{props.label}
</Text>
)}
<Text color={Colors.Black} weight={props.label ? 'semibold' : 'regular'}>
{props.text}
</Text>
</Column>
);
};
interface TextItemProps {
text: string;
label?: string;
divider?: boolean;
margin?: string;
}

3
components/ui/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export { Text } from './Text';
export { Button } from './Button';
export { Row, Column, Centered } from './Layout';

View File

@@ -0,0 +1,50 @@
import { ViewStyle } from 'react-native';
export const Colors = {
Black: '#231F20',
Grey: '#B0B0B0',
Grey5: '#E0E0E0',
Grey6: '#F2F2F2',
Orange: '#F2811D',
LightGrey: '#FAF9FF',
White: '#FFFFFF',
Red: '#EB5757',
Green: '#219653',
};
export function spacing(type: 'margin' | 'padding', values: string) {
const [top, end, bottom, start] = values.split(' ').map(Number);
return {
[`${type}Top`]: top,
[`${type}End`]: end != null ? end : top,
[`${type}Bottom`]: bottom != null ? bottom : top,
[`${type}Start`]: start != null ? start : end != null ? end : top,
};
}
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
export function elevation(level: ElevationLevel): ViewStyle {
// https://ethercreative.github.io/react-native-shadow-generator/
if (level === 0) {
return null;
}
const index = level - 1;
return {
shadowColor: Colors.Black,
shadowOffset: {
width: 0,
height: [1, 1, 1, 2, 2][index],
},
shadowOpacity: [0.18, 0.2, 0.22, 0.23, 0.25][index],
shadowRadius: [1.0, 1.41, 2.22, 2.62, 3.84][index],
elevation: level,
zIndex: level,
borderRadius: 4,
backgroundColor: Colors.White,
};
}

1
docs/images/README.md Normal file
View File

@@ -0,0 +1 @@
All images used in documentation.

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 504.35 108.15"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#ff6d37;}</style></defs><title>ID PASS Logo Artwork Orange Black</title><path class="cls-1" d="M337.4,246.18h38.23a75.48,75.48,0,0,1,15.12,1.45,34.27,34.27,0,0,1,12.42,5,25.37,25.37,0,0,1,8.44,9.59q3.12,6,3.12,15.19t-2.91,15.12a24.8,24.8,0,0,1-8,9.74,32.59,32.59,0,0,1-12.07,5.16A70.54,70.54,0,0,1,376.64,309H360.07V349.1H337.4Zm22.67,43.61h15.12a29.79,29.79,0,0,0,5.89-.58,15.61,15.61,0,0,0,5.09-2,10.46,10.46,0,0,0,3.63-3.78,11.9,11.9,0,0,0,1.38-6,10.22,10.22,0,0,0-1.82-6.32,12,12,0,0,0-4.65-3.71,19.86,19.86,0,0,0-6.32-1.67,65.37,65.37,0,0,0-6.69-.36H360.07Z" transform="translate(-168.77 -243.56)"/><path class="cls-1" d="M449.26,246.18H468L512.78,349.1H487.2l-8.87-21.81h-40l-8.57,21.81h-25ZM458,276.12l-12.5,32h25.15Z" transform="translate(-168.77 -243.56)"/><path class="cls-1" d="M574,271.33a16.09,16.09,0,0,0-7.49-5.16,26.72,26.72,0,0,0-8.94-1.68,23.26,23.26,0,0,0-5.08.59,19.3,19.3,0,0,0-5,1.81,10.8,10.8,0,0,0-3.77,3.27,8.3,8.3,0,0,0-1.46,4.95,8.18,8.18,0,0,0,3.49,7.12,33,33,0,0,0,8.79,4.21q5.31,1.76,11.42,3.49a47.44,47.44,0,0,1,11.41,4.95,27,27,0,0,1,8.79,8.57q3.49,5.39,3.49,14.39a33.16,33.16,0,0,1-3.2,15,30.33,30.33,0,0,1-8.65,10.61,36.86,36.86,0,0,1-12.64,6.25,58.1,58.1,0,0,1-33.8-1,46.56,46.56,0,0,1-16-9.88L531.53,321a23.64,23.64,0,0,0,8.8,7.2,24.79,24.79,0,0,0,16.57,1.89,18.23,18.23,0,0,0,5.09-2,10.91,10.91,0,0,0,3.63-3.34A8.32,8.32,0,0,0,567,320a8.7,8.7,0,0,0-3.56-7.34,32.78,32.78,0,0,0-8.94-4.58q-5.37-1.87-11.63-3.77a52.37,52.37,0,0,1-11.63-5.09,28.21,28.21,0,0,1-8.94-8.43q-3.56-5.24-3.56-13.81A30.93,30.93,0,0,1,522,262.46,31.56,31.56,0,0,1,530.73,252a37.89,37.89,0,0,1,12.58-6.32A51.29,51.29,0,0,1,558,243.56,57.9,57.9,0,0,1,574.85,246a38.89,38.89,0,0,1,14.69,8.29Z" transform="translate(-168.77 -243.56)"/><path class="cls-1" d="M657.42,271.33a16.09,16.09,0,0,0-7.49-5.16,26.72,26.72,0,0,0-8.94-1.68,23.19,23.19,0,0,0-5.08.59,19.3,19.3,0,0,0-4.95,1.81,10.92,10.92,0,0,0-3.78,3.27,8.3,8.3,0,0,0-1.45,4.95,8.18,8.18,0,0,0,3.49,7.12,32.86,32.86,0,0,0,8.79,4.21q5.31,1.76,11.42,3.49a47.44,47.44,0,0,1,11.41,4.95,27,27,0,0,1,8.79,8.57q3.5,5.39,3.49,14.39a33.16,33.16,0,0,1-3.2,15,30.33,30.33,0,0,1-8.65,10.61,36.86,36.86,0,0,1-12.64,6.25,58.1,58.1,0,0,1-33.8-1,46.56,46.56,0,0,1-16-9.88L615,321a23.64,23.64,0,0,0,8.8,7.2,24.79,24.79,0,0,0,16.57,1.89,18.23,18.23,0,0,0,5.09-2,10.91,10.91,0,0,0,3.63-3.34,8.32,8.32,0,0,0,1.38-4.8,8.7,8.7,0,0,0-3.56-7.34,32.78,32.78,0,0,0-8.94-4.58q-5.38-1.87-11.63-3.77a52.37,52.37,0,0,1-11.63-5.09,28.21,28.21,0,0,1-8.94-8.43q-3.56-5.24-3.56-13.81a30.93,30.93,0,0,1,3.27-14.54A31.56,31.56,0,0,1,614.17,252a37.89,37.89,0,0,1,12.58-6.32,51.29,51.29,0,0,1,14.68-2.11A58,58,0,0,1,658.29,246,38.85,38.85,0,0,1,673,254.32Z" transform="translate(-168.77 -243.56)"/><rect class="cls-2" y="2.62" width="22.68" height="20.93"/><polygon class="cls-2" points="0 34.89 0 42.88 0 105.54 22.68 105.54 22.68 42.88 22.68 34.89 0 34.89"/><path class="cls-2" d="M304.18,273.94A43.78,43.78,0,0,0,291,257.88a53.65,53.65,0,0,0-19.4-8.94,97.2,97.2,0,0,0-23.48-2.76h-34v70.65H236.8V267.11h13.37a50.18,50.18,0,0,1,13.6,1.82A33.44,33.44,0,0,1,275,274.38a26.26,26.26,0,0,1,7.63,9.23,28.78,28.78,0,0,1,2.84,13.16,34.52,34.52,0,0,1-2.84,14.75,25.48,25.48,0,0,1-7.77,9.74,31.35,31.35,0,0,1-11.71,5.31,63.62,63.62,0,0,1-14.6,1.59H214.12V349.1h37.07a71.65,71.65,0,0,0,21.95-3.34,56.1,56.1,0,0,0,18.46-9.82,47.31,47.31,0,0,0,12.72-16,48.91,48.91,0,0,0,4.73-22Q309.05,283.83,304.18,273.94Z" transform="translate(-168.77 -243.56)"/></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1 @@
<svg id="OWLNL" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1190.55 813.54"><defs><style>.cls-1,.cls-2{fill:#231f20;}.cls-1{fill-rule:evenodd;}.cls-3{fill:none;}</style></defs><title>newlogic logo black</title><path class="cls-1" d="M866.32,300.27s45.95-9.6,62-48c26.75-64-35.41-121.46-35.41-121.46C850.8,249,791.21,287.42,786.88,290.1q-2.34-7-5.15-13.76a42.5,42.5,0,0,0,26.14-39.23H760a202,202,0,0,0-329.39,0H382.68a42.51,42.51,0,0,0,26.11,39.22q-2.82,6.76-5.15,13.77c-4.33-2.67-63.93-41.09-106-159.29,0,0-62.16,57.46-35.41,121.46,16.05,38.4,62,48,62,48s-32.32,13-69.07-10.5c0,0-.25,42.23,28.32,66,28.25,23.5,65,14.75,65,14.75s-17.37,24.37-54.25,18.75c0,0,12.45,23.47,38,33.25,28.75,11,60,3.88,60,3.88s-8.25,24.37-55.5,18.87c0,0,23.75,27.5,55,41.5,26.69,12,61.22,15.05,66.35,15.45a201.91,201.91,0,0,0,274.43,0c5.13-.4,39.67-3.5,66.35-15.45,31.25-14,55-41.5,55-41.5-47.25,5.5-55.5-18.87-55.5-18.87s31.25,7.12,60-3.88c25.55-9.78,38-33.25,38-33.25-36.88,5.62-54.25-18.75-54.25-18.75s36.75,8.75,65-14.75c28.57-23.77,28.36-66,28.36-66C898.68,313.27,866.32,300.27,866.32,300.27ZM595.26,184.09a169.48,169.48,0,0,1,123.32,53H690.94A106.3,106.3,0,0,0,595.28,297a106.32,106.32,0,0,0-95.67-59.91H471.93A169.48,169.48,0,0,1,595.26,184.09ZM499.61,269a74.41,74.41,0,1,1-74.41,74.41A74.41,74.41,0,0,1,499.61,269Zm95.65,255a169.92,169.92,0,0,1-149.39-88.85,106.31,106.31,0,0,0,130-17.64L595.26,471l19.45-53.49a106.3,106.3,0,0,0,129.91,17.69A169.91,169.91,0,0,1,595.26,524Zm95.68-106.17a74.41,74.41,0,1,1,74.41-74.41A74.41,74.41,0,0,1,690.94,417.82Z"/><path class="cls-1" d="M512.89,385.93a29.24,29.24,0,1,0-29.23-29.24A29.24,29.24,0,0,0,512.89,385.93Zm-9-47.84a9.57,9.57,0,1,1-9.57,9.57A9.58,9.58,0,0,1,503.86,338.09Z"/><path class="cls-1" d="M677.66,327.46a29.24,29.24,0,1,0,29.23,29.23A29.23,29.23,0,0,0,677.66,327.46Zm-9,29.77a9.57,9.57,0,1,1,9.57-9.57A9.57,9.57,0,0,1,668.62,357.23Z"/><path class="cls-2" d="M191.34,705.12V619.75h17.32l40,52.56V619.75H267.2v85.37h-16l-41.34-54.27v54.27Z"/><path class="cls-2" d="M310.06,705.12V619.75h64.4v16.71H328.72v17.32H369v16.71H328.72v17.92h46.35v16.71Z"/><path class="cls-2" d="M435.17,705.73l-29.15-86h20.13l17.69,57.81L463,619.5h16.1l19.15,58.06,17.68-57.81h19.64l-29.15,86H490.06l-19.27-55.86-19.27,55.86Z"/><path class="cls-2" d="M564.33,705.12V619.75h18.79v68.3h42.56v17.07Z"/><path class="cls-2" d="M720,693.72q-13,12.87-32.5,12.87t-32.38-12.75a42.16,42.16,0,0,1-12.87-31.16v-.24a42.4,42.4,0,0,1,13-31.29q13-12.87,32.5-12.86T720.09,631A42.21,42.21,0,0,1,733,662.19v.25A42.39,42.39,0,0,1,720,693.72Zm-50.79-12.26a24.32,24.32,0,0,0,18.54,7.81q11.22,0,18.41-7.69a26.59,26.59,0,0,0,7.2-18.9v-.24a26.78,26.78,0,0,0-7.32-19,24.31,24.31,0,0,0-18.54-7.8q-11.22,0-18.41,7.68a26.59,26.59,0,0,0-7.2,18.9v.25A26.77,26.77,0,0,0,669.17,681.46Z"/><path class="cls-2" d="M801.4,706.59q-19.75,0-32.32-12.44t-12.57-31.47v-.24a42.73,42.73,0,0,1,12.81-31.29q12.81-12.87,31.95-12.86,11.1,0,18.79,2.86A50,50,0,0,1,834.81,630L823,644.27a40.27,40.27,0,0,0-10.36-6.59,31.22,31.22,0,0,0-12-2.07,22.64,22.64,0,0,0-17.37,7.8,26.84,26.84,0,0,0-7.14,18.78v.25q0,11.7,7.26,19.38t18.6,7.69q10.49,0,17.68-5.12v-12.2h-18.9V656h37.08v37.08A54.88,54.88,0,0,1,801.4,706.59Z"/><path class="cls-2" d="M872.5,705.12V619.75h18.78v85.37Z"/><path class="cls-2" d="M964.21,706.59q-18.78,0-31.28-12.69t-12.5-31.22v-.24q0-18.54,12.56-31.35t32-12.8q11.34,0,19.26,3.35a45,45,0,0,1,14.39,9.82l-11.94,13.78q-10.62-9.63-21.84-9.63a22.92,22.92,0,0,0-17.68,7.74,26.92,26.92,0,0,0-7.08,18.84v.25a27.41,27.41,0,0,0,7,19,22.81,22.81,0,0,0,17.74,7.81,26.63,26.63,0,0,0,11.83-2.44,46.43,46.43,0,0,0,10.62-7.57l11.94,12.08A50.2,50.2,0,0,1,984,702.74Q975.92,706.58,964.21,706.59Z"/><rect class="cls-3" width="1190.55" height="813.54"/></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

26
eas.json Normal file
View File

@@ -0,0 +1,26 @@
{
"cli": {
"version": ">= 0.37.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"ph": {
"developmentClient": true,
"distribution": "internal"
},
"newlogic": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {}
}
}

9
index.android.js Normal file
View File

@@ -0,0 +1,9 @@
import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

9
index.js Normal file
View File

@@ -0,0 +1,9 @@
import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

Some files were not shown because too many files have changed in this diff Show More