From: qiaowei Date: Fri, 27 Mar 2020 04:40:19 +0000 (+0800) Subject: 签约流程,指纹登录 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=c5a87f7321c1135f72139c3497ad8bda56e83695;p=dali_platform%2Fmobile.git 签约流程,指纹登录 --- diff --git a/config.xml b/config.xml index 2eab5ec..6283731 100644 --- a/config.xml +++ b/config.xml @@ -45,14 +45,9 @@ APP需要使用您的相册权限,没有该权限将无法完成扫一扫功能 - - + - - - - diff --git a/package-lock.json b/package-lock.json index 78076a9..ab7c5fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -300,9 +300,9 @@ "integrity": "sha1-SWBrjBWlaI1HKPkuSnMloGHeB/U=" }, "cordova-plugin-fingerprint-aio": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/cordova-plugin-fingerprint-aio/-/cordova-plugin-fingerprint-aio-1.7.0.tgz", - "integrity": "sha512-pV+JUeEhZUP85nXs9KMpHRClZppVHnpZA5h+8ReKBi4AV4vnzAcpUDBgFN7g3Ra83zmREYI/MqrlZlXn16pg3g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cordova-plugin-fingerprint-aio/-/cordova-plugin-fingerprint-aio-3.0.1.tgz", + "integrity": "sha512-Sgbv8Ykk74Ov1foEZg77owIqmHZoGktsv41rmW8KAAMf2JE93AawvQdUCYYROnk+ozG3D9ItELa5yhYU8i7Zhw==", "requires": { "cordova-plugin-add-swift-support": "^2.0.2" } @@ -997,9 +997,9 @@ "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA=" }, "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "send": { "version": "0.17.1", diff --git a/package.json b/package.json index e358ecb..5a54e09 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "cordova-plugin-device": "^2.0.2", "cordova-plugin-disable-ios11-statusbar": "^1.0.0", "cordova-plugin-file": "^6.0.1", - "cordova-plugin-fingerprint-aio": "^1.7.0", + "cordova-plugin-fingerprint-aio": "3.0.1", "cordova-plugin-inappbrowser": "^3.0.0", "cordova-plugin-jcore": "^1.3.0", "cordova-plugin-qrscanner": "^3.0.1", @@ -35,9 +35,6 @@ }, "cordova": { "plugins": { - "cordova-plugin-fingerprint-aio": { - "FACEID_USAGE_DESCRIPTION": "认证中..." - }, "cordova-plugin-touch-id": {}, "cordova-plugin-whitelist": {}, "cordova-plugin-advanced-http": { @@ -57,7 +54,10 @@ "cordova-plugin-app-version": {}, "cordova-plugin-wkwebview-engine": {}, "cordova-plugin-brightness": {}, - "cordova-plugin-file": {} + "cordova-plugin-file": {}, + "cordova-plugin-fingerprint-aio": { + "FACEID_USAGE_DESCRIPTION": " " + } }, "platforms": [ "android", diff --git a/platforms/android/android.json b/platforms/android/android.json index 2b20a7d..f684e89 100644 --- a/platforms/android/android.json +++ b/platforms/android/android.json @@ -8,10 +8,6 @@ "res/xml/config.xml": { "parents": { "/*": [ - { - "xml": "", - "count": 1 - }, { "xml": "", "count": 1 @@ -63,6 +59,10 @@ { "xml": "", "count": 1 + }, + { + "xml": "", + "count": 1 } ] } @@ -70,10 +70,6 @@ "AndroidManifest.xml": { "parents": { "/*": [ - { - "xml": "", - "count": 1 - }, { "xml": "", "count": 2 @@ -89,6 +85,14 @@ { "xml": "", "count": 1 + }, + { + "xml": "", + "count": 1 + }, + { + "xml": "", + "count": 1 } ], "/manifest": [ @@ -231,6 +235,10 @@ { "xml": "", "count": 1 + }, + { + "xml": "", + "count": 1 } ] } @@ -261,9 +269,6 @@ "cordova-plugin-add-swift-support": { "PACKAGE_NAME": "com.dalicitycard.app" }, - "cordova-plugin-fingerprint-aio": { - "PACKAGE_NAME": "com.dalicitycard.app" - }, "cordova-plugin-touch-id": { "PACKAGE_NAME": "com.dalicitycard.app" }, @@ -314,18 +319,13 @@ }, "cordova-plugin-brightness": { "PACKAGE_NAME": "com.dalicitycard.app" + }, + "cordova-plugin-fingerprint-aio": { + "PACKAGE_NAME": "com.dalicitycard.app" } }, "dependent_plugins": {}, "modules": [ - { - "id": "cordova-plugin-fingerprint-aio.Fingerprint", - "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js", - "pluginId": "cordova-plugin-fingerprint-aio", - "clobbers": [ - "Fingerprint" - ] - }, { "id": "cordova-plugin-touch-id.TouchID", "file": "plugins/cordova-plugin-touch-id/www/TouchID.js", @@ -661,11 +661,18 @@ "clobbers": [ "cordova.plugins.brightness" ] + }, + { + "id": "cordova-plugin-fingerprint-aio.Fingerprint", + "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js", + "pluginId": "cordova-plugin-fingerprint-aio", + "clobbers": [ + "Fingerprint" + ] } ], "plugin_metadata": { "cordova-plugin-add-swift-support": "2.0.2", - "cordova-plugin-fingerprint-aio": "1.7.0", "cordova-plugin-touch-id": "3.3.1", "cordova-plugin-whitelist": "1.3.3", "cordova-plugin-file": "6.0.1", @@ -681,6 +688,7 @@ "jpush-phonegap-plugin": "3.7.2", "cordova-plugin-app-version": "0.1.9", "cordova-plugin-wkwebview-engine": "1.2.1", - "cordova-plugin-brightness": "0.1.5" + "cordova-plugin-brightness": "0.1.5", + "cordova-plugin-fingerprint-aio": "3.0.1" } } diff --git a/platforms/android/app/build.gradle b/platforms/android/app/build.gradle index c6a120e..b59ebb2 100644 --- a/platforms/android/app/build.gradle +++ b/platforms/android/app/build.gradle @@ -91,6 +91,7 @@ ext { // PLUGIN GRADLE EXTENSIONS START apply from: "../cordova-plugin-qrscanner/dlapp-qrscanner.gradle" +apply from: "../cordova-plugin-fingerprint-aio/app-build.gradle" // PLUGIN GRADLE EXTENSIONS END def hasBuildExtras1 = file('build-extras.gradle').exists() @@ -175,12 +176,12 @@ android { } versionCode 12 versionName '1.3.8' - targetSdkVersion 26 + targetSdkVersion 28 } lintOptions { abortOnError false } - compileSdkVersion 26 + compileSdkVersion 28 buildToolsVersion '28.0.3' // This code exists for Crosswalk and other Native APIs. // By default, we multiply the existing version code in the diff --git a/platforms/android/app/release/app-release.apk b/platforms/android/app/release/app-release.apk index f62d122..6a0228e 100644 Binary files a/platforms/android/app/release/app-release.apk and b/platforms/android/app/release/app-release.apk differ diff --git a/platforms/android/app/src/main/AndroidManifest.xml b/platforms/android/app/src/main/AndroidManifest.xml index 591c860..556e0db 100644 --- a/platforms/android/app/src/main/AndroidManifest.xml +++ b/platforms/android/app/src/main/AndroidManifest.xml @@ -80,8 +80,8 @@ + - @@ -104,4 +104,6 @@ + + diff --git a/platforms/android/app/src/main/assets/www/cordova_plugins.js b/platforms/android/app/src/main/assets/www/cordova_plugins.js index fb0347b..dbec6c3 100644 --- a/platforms/android/app/src/main/assets/www/cordova_plugins.js +++ b/platforms/android/app/src/main/assets/www/cordova_plugins.js @@ -1,13 +1,5 @@ cordova.define('cordova/plugin_list', function(require, exports, module) { module.exports = [ - { - "id": "cordova-plugin-fingerprint-aio.Fingerprint", - "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js", - "pluginId": "cordova-plugin-fingerprint-aio", - "clobbers": [ - "Fingerprint" - ] - }, { "id": "cordova-plugin-touch-id.TouchID", "file": "plugins/cordova-plugin-touch-id/www/TouchID.js", @@ -345,41 +337,16 @@ cordova.define('cordova/plugin_list', function(require, exports, module) { ] }, { - "id": "cordova-plugin-file-opener2.FileOpener2", - "file": "plugins/cordova-plugin-file-opener2/www/plugins.FileOpener2.js", - "pluginId": "cordova-plugin-file-opener2", - "clobbers": [ - "cordova.plugins.fileOpener2" - ] - }, - { - "id": "cordova-plugin-file-transfer.FileTransferError", - "file": "plugins/cordova-plugin-file-transfer/www/FileTransferError.js", - "pluginId": "cordova-plugin-file-transfer", - "clobbers": [ - "window.FileTransferError" - ] - }, - { - "id": "cordova-plugin-file-transfer.FileTransfer", - "file": "plugins/cordova-plugin-file-transfer/www/FileTransfer.js", - "pluginId": "cordova-plugin-file-transfer", - "clobbers": [ - "window.FileTransfer" - ] - }, - { - "id": "cordova-plugin-crop.CropPlugin", - "file": "plugins/cordova-plugin-crop/www/crop.js", - "pluginId": "cordova-plugin-crop", + "id": "cordova-plugin-fingerprint-aio.Fingerprint", + "file": "plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js", + "pluginId": "cordova-plugin-fingerprint-aio", "clobbers": [ - "plugins.crop" + "Fingerprint" ] } ]; module.exports.metadata = { "cordova-plugin-add-swift-support": "2.0.2", - "cordova-plugin-fingerprint-aio": "1.7.0", "cordova-plugin-touch-id": "3.3.1", "cordova-plugin-whitelist": "1.3.3", "cordova-plugin-file": "6.0.1", @@ -396,8 +363,6 @@ cordova.define('cordova/plugin_list', function(require, exports, module) { "cordova-plugin-app-version": "0.1.9", "cordova-plugin-wkwebview-engine": "1.2.1", "cordova-plugin-brightness": "0.1.5", - "cordova-plugin-file-opener2": "2.2.1", - "cordova-plugin-file-transfer": "1.7.1", - "cordova-plugin-crop": "0.3.1" + "cordova-plugin-fingerprint-aio": "3.0.1" }; }); \ No newline at end of file diff --git a/platforms/android/app/src/main/assets/www/js/bindcard.js b/platforms/android/app/src/main/assets/www/js/bindcard.js index f7e91a2..7af7d38 100644 --- a/platforms/android/app/src/main/assets/www/js/bindcard.js +++ b/platforms/android/app/src/main/assets/www/js/bindcard.js @@ -81,9 +81,15 @@ var app = { window.localStorage.setItem("idno", idno); window.localStorage.setItem("phoneX",ret.phonex); window.localStorage.setItem("personid", ret.personid); + window.localStorage.setItem("userid", ret.personid); window.localStorage.setItem("signed", ret.signed); window.localStorage.setItem("paypwdset", ret.paypwdset); - window.location = "bindcheck.html"; + if(ret.signed=='yes'){ + window.location = "main.html" + }else{ + window.location = "bindcheck.html"; + } + } else { if (ret.code == -1) { $.alert(ret.msg, "提示", function() { diff --git a/platforms/android/app/src/main/assets/www/js/login.js b/platforms/android/app/src/main/assets/www/js/login.js index 7931a48..88950f3 100644 --- a/platforms/android/app/src/main/assets/www/js/login.js +++ b/platforms/android/app/src/main/assets/www/js/login.js @@ -7,6 +7,29 @@ var app = { onDeviceReady: function() { this.receivedEvent('deviceready'); + this.loginWithFinger(); + }, + loginWithFinger:function(){ + let loginfinger = window.localStorage.getItem("loginfinger"); + let phone = window.localStorage.getItem("phone"); + let pwd = window.localStorage.getItem("localpwd"); + let _this = this; + if(phone&&pwd&&loginfinger&&loginfinger=='1'){ + Fingerprint.isAvailable(function(result){ + Fingerprint.show({ + title:"正在使用指纹或人脸登录", + description: "请验证您的指纹或人脸" + }, function(success){ + _this.loginIt(phone,pwd); + }, function(error){ + $.alert("验证失败,指纹或人脸错误,请使用密码登录", "提示"); + //$("#login").removeAttr("checked"); + }); + + }, function(message){ + $.alert("设备不支持指纹或人脸,请使用密码登录", "提示"); + }); + } }, // Update DOM on a Received Event @@ -29,7 +52,9 @@ var app = { if(isEmpty(phone)||isEmpty(pwd)){ return; } - //loadingElement('loginBtn', '登录中...') + this.loginIt(phone,pwd); + }, + loginIt: function(phone,pwd){ $.showLoading("登录中"); var param={ "username":phone, @@ -56,6 +81,7 @@ var app = { window.localStorage.setItem("signed",ret.signed); window.localStorage.setItem("paypwdset",ret.paypwdset); window.localStorage.setItem("name",ret.name); + window.localStorage.setItem("localpwd",pwd); window.location = "main.html"; } }else{ diff --git a/platforms/android/app/src/main/assets/www/js/main.js b/platforms/android/app/src/main/assets/www/js/main.js index bc93d91..8c211e9 100644 --- a/platforms/android/app/src/main/assets/www/js/main.js +++ b/platforms/android/app/src/main/assets/www/js/main.js @@ -111,6 +111,25 @@ var app = { }) }) }); + $('#accsignbtn').click(function() { + app.checkBefore(function() { + var signed = window.localStorage.getItem("signed"); + var cum = new auiDialog({}); + if (isEmpty(signed) || signed != 'yes') { + var confirm = cum.alert({ + title: "提示", + msg: '您尚未签约市民卡免密代扣签约协议,无法使用该功能', + buttons: ['取消', '去签约'] + }, function(ret) { + if (ret.buttonIndex == 2) { + app.toSignCode(); + } + }) + }else{ + window.location = 'signxycheck.html' + } + }) + }); this.initData(); this.backBtn(); }, @@ -153,13 +172,15 @@ var app = { $("#loaddata").show() $("#nodata").hide(); var param = { - "pageno": 1 + "pageno": 1, + "platform":"android" } V1Bills(param, function(ok, ret) { if (ok) { - //console.log(ret) + console.log(ret) if (ret.code == 200) { - app.checkVersion(ret.version,ret.minversion,ret.versionmsg); + //app.checkVersion(ret.version,ret.minversion,ret.versionmsg,ret.versionurl); + app.checkVersion("1.3.9","1","new func","https://shouji.baidu.com/software/26706357.html"); $("#maingt").text(ret.t + "!") $("#user-amount").text(ret.amount) $("#user-point").text(ret.point) @@ -196,14 +217,21 @@ var app = { } }) }, - checkVersion:function(ver,minver,msg){ + checkVersion:function(ver,minver,m,url){ cordova.getAppVersion.getVersionNumber(function (version) { //alert(version); - if(versionversion){ - alert("有新的版本"); + alert(version); + if(ver>version){ + var cum = new auiDialog({}); + var confirm = cum.alert({ + title: "有新的版本", + msg: m, + buttons: ['取消', '去更新'] + }, function(ret) { + if (ret.buttonIndex == 2) { + cordova.InAppBrowser.open(url, '_system', 'location=no,toolbar=yes,toolbarposition=top,closebuttoncaption=关闭'); + } + }) } }); }, @@ -282,38 +310,40 @@ var app = { } }, checkOther: function(callback) { - var payseted = window.localStorage.getItem("paypwdset"); var signed = window.localStorage.getItem("signed"); var cum = new auiDialog({}); - console.log(payseted, signed) - if (isEmpty(payseted) || !payseted || payseted != 'true') { + + if (isEmpty(signed) || signed != 'yes') { var confirm = cum.alert({ title: "提示", - msg: '您还没有设置支付密码,无法使用该功能', - buttons: ['取消', '去设置'] + msg: '您尚未签约市民卡免密代扣签约协议,无法使用该功能', + buttons: ['取消', '去签约'] }, function(ret) { if (ret.buttonIndex == 2) { - window.location = 'paypwdset.html' + app.toSignCode(); } }) } else { - if (isEmpty(signed) || signed != 'yes') { - var confirm = cum.alert({ - title: "提示", - msg: '您尚未签约代扣免密付协议,无法使用该功能', - buttons: ['取消', '去签约'] - }, function(ret) { - if (ret.buttonIndex == 2) { - window.location = 'signxy.html' - } - }) - } else { - if (callback) { - callback() - } + if (callback) { + callback() } } }, + toSignCode: function(){ + var param = { + } + V1Bindcardcode(param, function(ok, ret) { + if (ok) { + if (ret.code == 200) { + window.location = 'bindcheck.html' + } else { + $.alert(ret.msg, "错误"); + } + } else { + $.alert("请求失败了 " + ret.status + ",请稍后再试", "错误"); + } + }) + }, toSign: function() { window.location = 'signxycheck.html' }, diff --git a/platforms/android/app/src/main/assets/www/js/qrcode.js b/platforms/android/app/src/main/assets/www/js/qrcode.js index b9d81b9..51b2aa1 100644 --- a/platforms/android/app/src/main/assets/www/js/qrcode.js +++ b/platforms/android/app/src/main/assets/www/js/qrcode.js @@ -22,6 +22,10 @@ var app = { console.log(err); }); } + document.addEventListener("backbutton", function(e){ + e.preventDefault(); + app.backTo(); + }, false); }, loadQrcode: function() { $.showLoading("加载中"); @@ -62,6 +66,7 @@ var app = { if (ok) { if (ret.code == 200) { $.hideLoading(); + $("#qrcode").html(''); var qrcode = new QRCode(document.getElementById("qrcode"), { text: ret.qrcode, width: 200, diff --git a/platforms/android/app/src/main/assets/www/js/security.js b/platforms/android/app/src/main/assets/www/js/security.js index f12a1cc..d159a40 100644 --- a/platforms/android/app/src/main/assets/www/js/security.js +++ b/platforms/android/app/src/main/assets/www/js/security.js @@ -5,23 +5,68 @@ var app = { document.addEventListener('deviceready', this.onDeviceReady.bind(this), false); }, onDeviceReady: function() { + let loginfinger = window.localStorage.getItem("loginfinger"); + if(loginfinger&&loginfinger=='1'){ + $("#login").prop("checked","checked"); + } }, editPwd: function() { window.location = "editpwd.html"; }, toPayPwd: function() { - window.location = "paypwdmng.html"; + var payseted = window.localStorage.getItem("paypwdset"); + if (isEmpty(payseted) || !payseted || payseted != 'true') { + window.location = 'paypwdset.html' + } else{ + window.location = "paypwdmng.html"; + } }, logout:function(){ window.localStorage.removeItem("token"); window.location = "login.html"; }, loginByTouchId: function() { - Fingerprint.isAvailable(function(result){ - alert("Fingerprint available"); - }, function(message){ - alert(message); - }); + let loginfinger = window.localStorage.getItem("loginfinger"); + if(loginfinger&&loginfinger=='1'){ + var confirm = new auiDialog({}).alert({ + title: "提示", + msg: '确定关闭指纹或人脸登录吗?', + buttons: ['取消', '确定'] + }, function(ret) { + if (ret.buttonIndex == 2) { + window.localStorage.setItem("loginfinger","0"); + $("#login").removeAttr("checked"); + }else{ + //$("#login").attr("checked","checked"); + } + }); + }else{ + let pwd = window.localStorage.getItem("localpwd"); + if(!pwd){ + $.alert("长时间未登录,需要先登录后再使用该功能", "提示",function(){ + window.localStorage.removeItem("token"); + window.location = "login.html"; + }); + return; + } + $("#login").removeAttr("checked"); + Fingerprint.isAvailable(function(result){ + Fingerprint.show({ + title:"大理市民卡开启指纹或人脸登录", + description: "请验证您的指纹或人脸" + }, function(success){ + window.localStorage.setItem("loginfinger","1"); + $.alert("验证成功,您已开启指纹或人脸登录", "提示"); + $("#login").prop("checked","checked"); + }, function(error){ + $.alert("验证失败,指纹或人脸错误次数过多,请稍后再试", "提示"); + //$("#login").removeAttr("checked"); + }); + + }, function(message){ + $.alert("设备不支持指纹或人脸", "提示"); + }); + } } }; app.initialize(); diff --git a/platforms/android/app/src/main/assets/www/js/server.js b/platforms/android/app/src/main/assets/www/js/server.js index ba99748..426bf2d 100644 --- a/platforms/android/app/src/main/assets/www/js/server.js +++ b/platforms/android/app/src/main/assets/www/js/server.js @@ -7,6 +7,10 @@ var CURRENT_INDEX=1; if (dev) { SERVER = "http://172.28.43.7:8099/payapi/mobileapi"; } +function V1Bindcardcode(param,callback) { + ajaxPost("/v1/bindcardcode", param, callback) +} + function V1Unbindcard(param,callback){ ajaxPost("/v1/unbindcard", param, callback) } diff --git a/platforms/android/app/src/main/assets/www/js/signxy.js b/platforms/android/app/src/main/assets/www/js/signxy.js index f97f3d4..4ee8598 100644 --- a/platforms/android/app/src/main/assets/www/js/signxy.js +++ b/platforms/android/app/src/main/assets/www/js/signxy.js @@ -14,10 +14,10 @@ var app = { if(ret.code==200){ $("#content").html(ret.page); if(isEmpty(ret.signed)||ret.signed!='yes'){ - $("#btn").show(); - $("#content").css("bottom","135px"); + //$("#btn").show(); + //$("#content").css("bottom","135px"); }else{ - $("#content").css("bottom","10px"); + //$("#content").css("bottom","10px"); } }else{ $.alert(ret.msg, "错误"); diff --git a/platforms/android/app/src/main/assets/www/main.html b/platforms/android/app/src/main/assets/www/main.html index b357ca3..c8bb590 100644 --- a/platforms/android/app/src/main/assets/www/main.html +++ b/platforms/android/app/src/main/assets/www/main.html @@ -222,7 +222,7 @@
-
  • +
  • diff --git a/platforms/android/app/src/main/assets/www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js b/platforms/android/app/src/main/assets/www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js index 3982139..22ecb6c 100644 --- a/platforms/android/app/src/main/assets/www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js +++ b/platforms/android/app/src/main/assets/www/plugins/cordova-plugin-fingerprint-aio/www/Fingerprint.js @@ -1,8 +1,28 @@ cordova.define("cordova-plugin-fingerprint-aio.Fingerprint", function(require, exports, module) { /*global cordova */ -function Fingerprint() { -} +var Fingerprint = function() { +}; + +// Plugin Errors +Fingerprint.prototype.BIOMETRIC_UNKNOWN_ERROR = -100; +Fingerprint.prototype.BIOMETRIC_UNAVAILABLE = -101; +Fingerprint.prototype.BIOMETRIC_AUTHENTICATION_FAILED = -102; +Fingerprint.prototype.BIOMETRIC_SDK_NOT_SUPPORTED = -103; +Fingerprint.prototype.BIOMETRIC_HARDWARE_NOT_SUPPORTED = -104; +Fingerprint.prototype.BIOMETRIC_PERMISSION_NOT_GRANTED = -105; +Fingerprint.prototype.BIOMETRIC_NOT_ENROLLED = -106; +Fingerprint.prototype.BIOMETRIC_INTERNAL_PLUGIN_ERROR = -107; +Fingerprint.prototype.BIOMETRIC_DISMISSED = -108; +Fingerprint.prototype.BIOMETRIC_PIN_OR_PATTERN_DISMISSED = -109; +Fingerprint.prototype.BIOMETRIC_SCREEN_GUARD_UNSECURED = -110; +Fingerprint.prototype.BIOMETRIC_LOCKED_OUT = -111; +Fingerprint.prototype.BIOMETRIC_LOCKED_OUT_PERMANENT = -112; + +// Biometric types +Fingerprint.prototype.BIOMETRIC_TYPE_FINGERPRINT = "finger"; +Fingerprint.prototype.BIOMETRIC_TYPE_FACE = "face"; +Fingerprint.prototype.BIOMETRIC_TYPE_COMMON = "biometric"; Fingerprint.prototype.show = function (params, successCallback, errorCallback) { cordova.exec( diff --git a/platforms/android/app/src/main/assets/www/qrcode.html b/platforms/android/app/src/main/assets/www/qrcode.html index 5ce5788..a64f153 100644 --- a/platforms/android/app/src/main/assets/www/qrcode.html +++ b/platforms/android/app/src/main/assets/www/qrcode.html @@ -24,9 +24,9 @@

    请将二维码对准扫描设备

    - +
    +
    刷新二维码
    +
    diff --git a/platforms/android/app/src/main/assets/www/security.html b/platforms/android/app/src/main/assets/www/security.html index 39a40ca..971cc59 100644 --- a/platforms/android/app/src/main/assets/www/security.html +++ b/platforms/android/app/src/main/assets/www/security.html @@ -31,15 +31,19 @@
  • - + + + +
    +
    • @@ -50,9 +54,6 @@
    -
    diff --git a/platforms/android/app/src/main/assets/www/signxy.html b/platforms/android/app/src/main/assets/www/signxy.html index cb0f1ba..c22eda3 100644 --- a/platforms/android/app/src/main/assets/www/signxy.html +++ b/platforms/android/app/src/main/assets/www/signxy.html @@ -11,31 +11,19 @@ - 签约代扣免密支付协议 + 市民卡免密代扣签约协议
    - + -
    签约代扣免密支付协议
    +
    市民卡免密代扣签约协议
    -
    +
    - + diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/BiometricActivity.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/BiometricActivity.java new file mode 100644 index 0000000..0d6d20e --- /dev/null +++ b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/BiometricActivity.java @@ -0,0 +1,159 @@ +package de.niklasmerz.cordova.biometric; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; + +import com.exxbrain.android.biometric.BiometricPrompt; + +import java.util.concurrent.Executor; + +public class BiometricActivity extends AppCompatActivity { + + private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 2; + private PromptInfo mPromptInfo; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setTitle(null); + int layout = getResources() + .getIdentifier("biometric_activity", "layout", getPackageName()); + setContentView(layout); + + if (savedInstanceState != null) { + return; + } + + mPromptInfo = new PromptInfo.Builder(getIntent().getExtras()).build(); + authenticate(); + + } + + private void authenticate() { + final Handler handler = new Handler(Looper.getMainLooper()); + Executor executor = handler::post; + + BiometricPrompt biometricPrompt = + new BiometricPrompt(this, executor, mAuthenticationCallback); + + BiometricPrompt.PromptInfo.Builder promptInfoBuilder = new BiometricPrompt.PromptInfo.Builder() + .setTitle(mPromptInfo.getTitle()) + .setSubtitle(mPromptInfo.getSubtitle()) + .setDescription(mPromptInfo.getDescription()); + + if (mPromptInfo.isDeviceCredentialAllowed() + && Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { // TODO: remove after fix https://issuetracker.google.com/issues/142740104 + promptInfoBuilder.setDeviceCredentialAllowed(true); + } else { + promptInfoBuilder.setNegativeButtonText(mPromptInfo.getCancelButtonTitle()); + } + + biometricPrompt.authenticate(promptInfoBuilder.build()); + } + + private BiometricPrompt.AuthenticationCallback mAuthenticationCallback = + new BiometricPrompt.AuthenticationCallback() { + + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + onError(errorCode, errString); + } + + @Override + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + finishWithSuccess(); + } + + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + } + }; + + + // TODO: remove after fix https://issuetracker.google.com/issues/142740104 + private void showAuthenticationScreen() { + KeyguardManager keyguardManager = ContextCompat + .getSystemService(this, KeyguardManager.class); + if (keyguardManager == null + || android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) { + return; + } + if (keyguardManager.isKeyguardSecure()) { + Intent intent = keyguardManager + .createConfirmDeviceCredentialIntent(mPromptInfo.getTitle(), mPromptInfo.getDescription()); + this.startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); + } else { + // Show a message that the user hasn't set up a lock screen. + finishWithError(PluginError.BIOMETRIC_SCREEN_GUARD_UNSECURED); + } + } + + // TODO: remove after fix https://issuetracker.google.com/issues/142740104 + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { + if (resultCode == Activity.RESULT_OK) { + finishWithSuccess(); + } else { + finishWithError(PluginError.BIOMETRIC_PIN_OR_PATTERN_DISMISSED); + } + } + } + + private void onError(int errorCode, @NonNull CharSequence errString) { + + switch (errorCode) + { + case BiometricPrompt.ERROR_USER_CANCELED: + case BiometricPrompt.ERROR_CANCELED: + finishWithError(PluginError.BIOMETRIC_DISMISSED); + return; + case BiometricPrompt.ERROR_NEGATIVE_BUTTON: + // TODO: remove after fix https://issuetracker.google.com/issues/142740104 + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P && mPromptInfo.isDeviceCredentialAllowed()) { + showAuthenticationScreen(); + return; + } + finishWithError(PluginError.BIOMETRIC_DISMISSED); + break; + case BiometricPrompt.ERROR_LOCKOUT: + finishWithError(PluginError.BIOMETRIC_LOCKED_OUT.getValue(), errString.toString()); + break; + case BiometricPrompt.ERROR_LOCKOUT_PERMANENT: + finishWithError(PluginError.BIOMETRIC_LOCKED_OUT_PERMANENT.getValue(), errString.toString()); + break; + default: + finishWithError(errorCode, errString.toString()); + } + } + + private void finishWithSuccess() { + setResult(RESULT_OK); + finish(); + } + + private void finishWithError(PluginError error) { + finishWithError(error.getValue(), error.getMessage()); + } + + private void finishWithError(int code, String message) { + Intent data = new Intent(); + data.putExtra("code", code); + data.putExtra("message", message); + setResult(RESULT_CANCELED, data); + finish(); + } +} diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/Fingerprint.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/Fingerprint.java new file mode 100644 index 0000000..5bfe78e --- /dev/null +++ b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/Fingerprint.java @@ -0,0 +1,132 @@ +package de.niklasmerz.cordova.biometric; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; + +import com.exxbrain.android.biometric.BiometricManager; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class Fingerprint extends CordovaPlugin { + + private static final String TAG = "Fingerprint"; + private CallbackContext mCallbackContext = null; + + private static final int REQUEST_CODE_BIOMETRIC = 1; + private PromptInfo.Builder mPromptInfoBuilder; + + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Log.v(TAG, "Init Fingerprint"); + mPromptInfoBuilder = new PromptInfo.Builder(cordova.getActivity()); + } + + public boolean execute(final String action, JSONArray args, CallbackContext callbackContext) { + + this.mCallbackContext = callbackContext; + Log.v(TAG, "Fingerprint action: " + action); + + if (action.equals("authenticate")) { + executeAuthenticate(args); + return true; + + } else if (action.equals("isAvailable")){ + executeIsAvailable(); + return true; + } + + return false; + } + + private void executeIsAvailable() { + PluginError error = canAuthenticate(); + if (error != null) { + sendError(error); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){ + sendSuccess("biometric"); + } else { + sendSuccess("finger"); + } + } + + private void executeAuthenticate(JSONArray args) { + PluginError error = canAuthenticate(); + if (error != null) { + sendError(error); + return; + } + cordova.getActivity().runOnUiThread(() -> { + mPromptInfoBuilder.parseArgs(args); + Intent intent = new Intent(cordova.getActivity().getApplicationContext(), BiometricActivity.class); + intent.putExtras(mPromptInfoBuilder.build().getBundle()); + this.cordova.startActivityForResult(this, intent, REQUEST_CODE_BIOMETRIC); + }); + PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT); + pluginResult.setKeepCallback(true); + this.mCallbackContext.sendPluginResult(pluginResult); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + if (requestCode != REQUEST_CODE_BIOMETRIC) { + return; + } + if (resultCode == Activity.RESULT_OK) { + sendSuccess("biometric_success"); + } else if (intent != null) { + Bundle extras = intent.getExtras(); + sendError(extras.getInt("code"), extras.getString("message")); + } else { + sendError(PluginError.BIOMETRIC_DISMISSED); + } + } + + private PluginError canAuthenticate() { + int error = BiometricManager.from(cordova.getContext()).canAuthenticate(); + switch (error) { + case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: + case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: + return PluginError.BIOMETRIC_HARDWARE_NOT_SUPPORTED; + case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: + return PluginError.BIOMETRIC_NOT_ENROLLED; + default: + return null; + } + } + + private void sendError(int code, String message) { + JSONObject resultJson = new JSONObject(); + try { + resultJson.put("code", code); + resultJson.put("message", message); + + PluginResult result = new PluginResult(PluginResult.Status.ERROR, resultJson); + result.setKeepCallback(true); + cordova.getActivity().runOnUiThread(() -> + Fingerprint.this.mCallbackContext.sendPluginResult(result)); + } catch (JSONException e) { + Log.e(TAG, e.getMessage(), e); + } + } + + private void sendError(PluginError error) { + sendError(error.getValue(), error.getMessage()); + } + + private void sendSuccess(String message) { + Log.e(TAG, message); + cordova.getActivity().runOnUiThread(() -> + this.mCallbackContext.success(message)); + } +} diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PluginError.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PluginError.java new file mode 100644 index 0000000..1a4a5db --- /dev/null +++ b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PluginError.java @@ -0,0 +1,35 @@ +package de.niklasmerz.cordova.biometric; + +public enum PluginError { + + BIOMETRIC_AUTHENTICATION_FAILED(-102, "Authentication failed"), + BIOMETRIC_HARDWARE_NOT_SUPPORTED(-104), + BIOMETRIC_NOT_ENROLLED(-106), + BIOMETRIC_DISMISSED(-108), + BIOMETRIC_PIN_OR_PATTERN_DISMISSED(-109), + BIOMETRIC_SCREEN_GUARD_UNSECURED(-110, + "Go to 'Settings -> Security -> Screenlock' to set up a lock screen"), + BIOMETRIC_LOCKED_OUT(-111), + BIOMETRIC_LOCKED_OUT_PERMANENT(-112); + + private int value; + private String message; + + PluginError(int value) { + this.value = value; + this.message = this.name(); + } + + PluginError(int value, String message) { + this.value = value; + this.message = message; + } + + public int getValue() { + return value; + } + + public String getMessage() { + return message; + } +} diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PromptInfo.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PromptInfo.java new file mode 100644 index 0000000..31ed0dd --- /dev/null +++ b/platforms/android/app/src/main/java/de/niklasmerz/cordova/biometric/PromptInfo.java @@ -0,0 +1,136 @@ +package de.niklasmerz.cordova.biometric; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +class PromptInfo { + + private static final String DISABLE_BACKUP = "disableBackup"; + private static final String TITLE = "title"; + private static final String SUBTITLE = "subtitle"; + private static final String DESCRIPTION = "description"; + private static final String FALLBACK_BUTTON_TITLE = "fallbackButtonTitle"; + private static final String CANCEL_BUTTON_TITLE = "cancelButtonTitle"; + + private Bundle bundle = new Bundle(); + + Bundle getBundle() { + return bundle; + } + + String getTitle() { + return bundle.getString(TITLE); + } + + String getSubtitle() { + return bundle.getString(SUBTITLE); + } + + String getDescription() { + return bundle.getString(DESCRIPTION); + } + + boolean isDeviceCredentialAllowed() { + return !bundle.getBoolean(DISABLE_BACKUP); + } + + String getFallbackButtonTitle() { + return bundle.getString(FALLBACK_BUTTON_TITLE); + } + + String getCancelButtonTitle() { + return bundle.getString(CANCEL_BUTTON_TITLE); + } + + public static final class Builder { + private static final String TAG = "PromptInfo.Builder"; + private Bundle bundle; + private boolean disableBackup = false; + private String title; + private String subtitle = null; + private String description = null; + private String fallbackButtonTitle = "Use backup"; + private String cancelButtonTitle = "Cancel"; + + Builder(Context context) { + PackageManager packageManager = context.getPackageManager(); + try { + ApplicationInfo app = packageManager + .getApplicationInfo(context.getPackageName(), 0); + title = packageManager.getApplicationLabel(app) + " Biometric Sign On"; + } catch (PackageManager.NameNotFoundException e) { + title = "Biometric Sign On"; + } + } + + Builder(Bundle bundle) { + this.bundle = bundle; + } + + public PromptInfo build() { + PromptInfo promptInfo = new PromptInfo(); + + if (this.bundle != null) { + promptInfo.bundle = bundle; + return promptInfo; + } + + Bundle bundle = new Bundle(); + bundle.putString(SUBTITLE, this.subtitle); + bundle.putString(TITLE, this.title); + bundle.putString(DESCRIPTION, this.description); + bundle.putString(FALLBACK_BUTTON_TITLE, this.fallbackButtonTitle); + bundle.putString(CANCEL_BUTTON_TITLE, this.cancelButtonTitle); + bundle.putBoolean(DISABLE_BACKUP, this.disableBackup); + promptInfo.bundle = bundle; + + return promptInfo; + } + + void parseArgs(JSONArray args) { + JSONObject argsObject; + try { + argsObject = args.getJSONObject(0); + } catch (JSONException e) { + Log.e(TAG, "Can't parse args. Defaults will be used.", e); + return; + } + disableBackup = getBooleanArg(argsObject, DISABLE_BACKUP, disableBackup); + title = getStringArg(argsObject, TITLE, title); + subtitle = getStringArg(argsObject, SUBTITLE, subtitle); + description = getStringArg(argsObject, DESCRIPTION, description); + fallbackButtonTitle = getStringArg(argsObject, FALLBACK_BUTTON_TITLE, "Use Backup"); + cancelButtonTitle = getStringArg(argsObject, CANCEL_BUTTON_TITLE, "Cancel"); + } + + private Boolean getBooleanArg(JSONObject argsObject, String name, Boolean defaultValue) { + if (argsObject.has(name)){ + try { + return argsObject.getBoolean(name); + } catch (JSONException e) { + Log.e(TAG, "Can't parse '" + name + "'. Default will be used.", e); + } + } + return defaultValue; + } + + private String getStringArg(JSONObject argsObject, String name, String defaultValue) { + if (argsObject.optString(name) != null + && !argsObject.optString(name).isEmpty()){ + try { + return argsObject.getString(name); + } catch (JSONException e) { + Log.e(TAG, "Can't parse '" + name + "'. Default will be used.", e); + } + } + return defaultValue; + } + } +} diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/Fingerprint.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/Fingerprint.java deleted file mode 100644 index 32fd67c..0000000 --- a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/Fingerprint.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) https://github.com/mjwheatley/cordova-plugin-android-fingerprint-auth - * Modifications copyright (C) 2016 Niklas Merz - * - * 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 - * - * http://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 - */ - -package de.niklasmerz.cordova.fingerprint; - -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.CordovaInterface; - -import android.annotation.TargetApi; -import android.app.FragmentTransaction; -import android.app.KeyguardManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Bundle; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyPermanentlyInvalidatedException; -import android.security.keystore.KeyProperties; -import android.util.Base64; -import android.util.DisplayMetrics; -import android.util.Log; - -import org.apache.cordova.PluginResult; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.Locale; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; - -@TargetApi(23) -public class Fingerprint extends CordovaPlugin { - - public static final String TAG = "Fingerprint"; - public static String packageName; - - private static final String DIALOG_FRAGMENT_TAG = "FpAuthDialog"; - private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; - - KeyguardManager mKeyguardManager; - FingerprintAuthenticationDialogFragment mFragment; - public static KeyStore mKeyStore; - public static KeyGenerator mKeyGenerator; - public static Cipher mCipher; - private FingerprintManager mFingerPrintManager; - - public static CallbackContext mCallbackContext; - public static PluginResult mPluginResult; - - /** - * Alias for our key in the Android Key Store - */ - private static String mClientId; - /** - * Used to encrypt token - */ - private static String mClientSecret; - - /** - * Options - */ - private static boolean mDisableBackup = false; - - /** - * Constructor. - */ - public Fingerprint() { - } - - /** - * Sets the context of the Command. This can then be used to do things like - * get file paths associated with the Activity. - * - * @param cordova The context of the main Activity. - * @param webView The CordovaWebView Cordova is running in. - */ - - public void initialize(CordovaInterface cordova, CordovaWebView webView) { - super.initialize(cordova, webView); - Log.v(TAG, "Init Fingerprint"); - packageName = cordova.getActivity().getApplicationContext().getPackageName(); - mPluginResult = new PluginResult(PluginResult.Status.NO_RESULT); - - if (android.os.Build.VERSION.SDK_INT < 23) { - return; - } - - mKeyguardManager = cordova.getActivity().getSystemService(KeyguardManager.class); - mFingerPrintManager = cordova.getActivity().getApplicationContext() - .getSystemService(FingerprintManager.class); - - try { - mKeyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); - mKeyStore = KeyStore.getInstance(ANDROID_KEY_STORE); - - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Failed to get an instance of KeyGenerator", e); - } catch (NoSuchProviderException e) { - throw new RuntimeException("Failed to get an instance of KeyGenerator", e); - } catch (KeyStoreException e) { - throw new RuntimeException("Failed to get an instance of KeyStore", e); - } - - try { - mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_CBC + "/" - + KeyProperties.ENCRYPTION_PADDING_PKCS7); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Failed to get an instance of Cipher", e); - } catch (NoSuchPaddingException e) { - throw new RuntimeException("Failed to get an instance of Cipher", e); - } - } - - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackContext The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public boolean execute(final String action, - JSONArray args, - CallbackContext callbackContext) throws JSONException { - mCallbackContext = callbackContext; - Log.v(TAG, "Fingerprint action: " + action); - if (android.os.Build.VERSION.SDK_INT < 23) { - Log.e(TAG, "minimum SDK version 23 required"); - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - mCallbackContext.error("minimum SDK version 23 required"); - mCallbackContext.sendPluginResult(mPluginResult); - return true; - } - - final JSONObject arg_object = args.getJSONObject(0); - - if (action.equals("authenticate")) { - if (!arg_object.has("clientId") || !arg_object.has("clientSecret")) { - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - mCallbackContext.error("Missing required parameters"); - mCallbackContext.sendPluginResult(mPluginResult); - return true; - } - mClientId = arg_object.getString("clientId"); - mClientSecret = arg_object.getString("clientSecret"); - if (arg_object.has("disableBackup")) { - mDisableBackup = arg_object.getBoolean("disableBackup"); - } - // Set language - Resources res = cordova.getActivity().getResources(); - // Change locale settings in the app. - DisplayMetrics dm = res.getDisplayMetrics(); - Configuration conf = res.getConfiguration(); - //Do not change locale - res.updateConfiguration(conf, dm); - - if (isFingerprintAuthAvailable()) { - SecretKey key = getSecretKey(); - boolean isCipherInit = true; - if (key == null) { - if (createKey()) { - key = getSecretKey(); - } - } - if (key != null && !initCipher()) { - isCipherInit = false; - } - if (key != null) { - cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - // Set up the crypto object for later. The object will be authenticated by use - // of the fingerprint. - mFragment = new FingerprintAuthenticationDialogFragment(); - Bundle bundle = new Bundle(); - bundle.putBoolean("disableBackup", mDisableBackup); - mFragment.setArguments(bundle); - - if (initCipher()) { - mFragment.setCancelable(false); - // Show the fingerprint dialog. The user has the option to use the fingerprint with - // crypto, or you can fall back to using a server-side verified password. - mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); - FragmentTransaction transaction = cordova.getActivity().getFragmentManager().beginTransaction(); - transaction.add(mFragment, DIALOG_FRAGMENT_TAG); - transaction.commitAllowingStateLoss(); - } else { - if (!mDisableBackup) { - // This happens if the lock screen has been disabled or or a fingerprint got - // enrolled. Thus show the dialog to authenticate with their password - mFragment.setCryptoObject(new FingerprintManager - .CryptoObject(mCipher)); - mFragment.setStage(FingerprintAuthenticationDialogFragment - .Stage.NEW_FINGERPRINT_ENROLLED); - FragmentTransaction transaction = cordova.getActivity().getFragmentManager().beginTransaction(); - transaction.add(mFragment, DIALOG_FRAGMENT_TAG); - transaction.commitAllowingStateLoss(); - } else { - mCallbackContext.error("Failed to init Cipher and backup disabled."); - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - mCallbackContext.sendPluginResult(mPluginResult); - } - } - } - }); - mPluginResult.setKeepCallback(true); - } else { - mCallbackContext.sendPluginResult(mPluginResult); - } - - } else { - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - mCallbackContext.error("Fingerprint authentication not available"); - mCallbackContext.sendPluginResult(mPluginResult); - } - return true; - } else if (action.equals("isAvailable")) { - if(isFingerprintAuthAvailable() && mFingerPrintManager.isHardwareDetected() && mFingerPrintManager.hasEnrolledFingerprints()){ - mPluginResult = new PluginResult(PluginResult.Status.OK, "finger"); - mCallbackContext.success("finger"); - }else{ - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - - if (mFingerPrintManager.isHardwareDetected() && !mFingerPrintManager.hasEnrolledFingerprints()) { - mCallbackContext.error("Fingerprint authentication not ready"); - } else { - mCallbackContext.error("Fingerprint authentication not available"); - } - } - mCallbackContext.sendPluginResult(mPluginResult); - return true; - } - return false; - } - - private boolean isFingerprintAuthAvailable() { - return mFingerPrintManager.isHardwareDetected() - && mFingerPrintManager.hasEnrolledFingerprints(); - } - - /** - * Initialize the {@link Cipher} instance with the created key in the {@link #createKey()} - * method. - * - * @return {@code true} if initialization is successful, {@code false} if the lock screen has - * been disabled or reset after the key was generated, or if a fingerprint got enrolled after - * the key was generated. - */ - private static boolean initCipher() { - boolean initCipher = false; - String errorMessage = ""; - String initCipherExceptionErrorPrefix = "Failed to init Cipher: "; - try { - SecretKey key = getSecretKey(); - mCipher.init(Cipher.ENCRYPT_MODE, key); - initCipher = true; - } catch (InvalidKeyException e) { - errorMessage = initCipherExceptionErrorPrefix + "InvalidKeyException: " + e.toString(); - - } - if (!initCipher) { - Log.e(TAG, errorMessage); - createKey(); - } - return initCipher; - } - - private static SecretKey getSecretKey() { - String errorMessage = ""; - String getSecretKeyExceptionErrorPrefix = "Failed to get SecretKey from KeyStore: "; - SecretKey key = null; - try { - mKeyStore.load(null); - key = (SecretKey) mKeyStore.getKey(mClientId, null); - } catch (KeyStoreException e) { - errorMessage = getSecretKeyExceptionErrorPrefix - + "KeyStoreException: " + e.toString();; - } catch (CertificateException e) { - errorMessage = getSecretKeyExceptionErrorPrefix - + "CertificateException: " + e.toString();; - } catch (UnrecoverableKeyException e) { - errorMessage = getSecretKeyExceptionErrorPrefix - + "UnrecoverableKeyException: " + e.toString();; - } catch (IOException e) { - errorMessage = getSecretKeyExceptionErrorPrefix - + "IOException: " + e.toString();; - } catch (NoSuchAlgorithmException e) { - errorMessage = getSecretKeyExceptionErrorPrefix - + "NoSuchAlgorithmException: " + e.toString();; - } - if (key == null) { - Log.e(TAG, errorMessage); - } - return key; - } - - /** - * Creates a symmetric key in the Android Key Store which can only be used after the user has - * authenticated with fingerprint. - */ - public static boolean createKey() { - String errorMessage = ""; - String createKeyExceptionErrorPrefix = "Failed to create key: "; - boolean isKeyCreated = false; - // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint - // for your flow. Use of keys is necessary if you need to know if the set of - // enrolled fingerprints has changed. - try { - mKeyStore.load(null); - // Set the alias of the entry in Android KeyStore where the key will appear - // and the constrains (purposes) in the constructor of the Builder - mKeyGenerator.init(new KeyGenParameterSpec.Builder(mClientId, - KeyProperties.PURPOSE_ENCRYPT | - KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - // Require the user to authenticate with a fingerprint to authorize every use - // of the key - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - mKeyGenerator.generateKey(); - isKeyCreated = true; - } catch (NoSuchAlgorithmException e) { - errorMessage = createKeyExceptionErrorPrefix - + "NoSuchAlgorithmException: " + e.toString();; - } catch (InvalidAlgorithmParameterException e) { - errorMessage = createKeyExceptionErrorPrefix - + "InvalidAlgorithmParameterException: " + e.toString();; - } catch (CertificateException e) { - errorMessage = createKeyExceptionErrorPrefix - + "CertificateException: " + e.toString();; - } catch (IOException e) { - errorMessage = createKeyExceptionErrorPrefix - + "IOException: " + e.toString();; - } - if (!isKeyCreated) { - Log.e(TAG, errorMessage); - setPluginResultError(errorMessage); - } - return isKeyCreated; - } - - public static void onAuthenticated(boolean withFingerprint) { - JSONObject resultJson = new JSONObject(); - String errorMessage = ""; - boolean createdResultJson = false; - try { - - if (withFingerprint) { - // If the user has authenticated with fingerprint, verify that using cryptography and - // then return the encrypted token - byte[] encrypted = tryEncrypt(); - resultJson.put("withFingerprint", Base64.encodeToString(encrypted, 0 /* flags */)); - } else { - // Authentication happened with backup password. - resultJson.put("withPassword", true); - - // if failed to init cipher because of InvalidKeyException, create new key - if (!initCipher()) { - createKey(); - } - } - createdResultJson = true; - } catch (BadPaddingException e) { - errorMessage = "Failed to encrypt the data with the generated key:" + - " BadPaddingException: " + e.getMessage(); - Log.e(TAG, errorMessage); - } catch (IllegalBlockSizeException e) { - errorMessage = "Failed to encrypt the data with the generated key: " + - "IllegalBlockSizeException: " + e.getMessage(); - Log.e(TAG, errorMessage); - } catch (JSONException e) { - errorMessage = "Failed to set resultJson key value pair: " + e.getMessage(); - Log.e(TAG, errorMessage); - } - - if (createdResultJson) { - mCallbackContext.success(resultJson); - mPluginResult = new PluginResult(PluginResult.Status.OK); - } else { - mCallbackContext.error(errorMessage); - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - } - mCallbackContext.sendPluginResult(mPluginResult); - } - - public static void onCancelled() { - mCallbackContext.error("Cancelled"); - } - - /** - * Tries to encrypt some data with the generated key in {@link #createKey} which is - * only works if the user has just authenticated via fingerprint. - */ - private static byte[] tryEncrypt() throws BadPaddingException, IllegalBlockSizeException { - return mCipher.doFinal(mClientSecret.getBytes()); - } - - public static boolean setPluginResultError(String errorMessage) { - mCallbackContext.error(errorMessage); - mPluginResult = new PluginResult(PluginResult.Status.ERROR); - return false; - } -} \ No newline at end of file diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintAuthenticationDialogFragment.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintAuthenticationDialogFragment.java deleted file mode 100644 index 54fb28d..0000000 --- a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintAuthenticationDialogFragment.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * Modifications copyright (C) 2016 Niklas Merz - * - * 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 - * - * http://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 - */ - -package de.niklasmerz.cordova.fingerprint; - -import android.app.DialogFragment; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -/** - * A dialog which uses fingerprint APIs to authenticate the user, and falls back to password - * authentication if fingerprint is not available. - */ -public class FingerprintAuthenticationDialogFragment extends DialogFragment - implements FingerprintUiHelper.Callback { - - private static final String TAG = "FingerprintAuthDialog"; - private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; - - private Button mCancelButton; - private Button mSecondDialogButton; - private View mFingerprintContent; - - private Stage mStage = Stage.FINGERPRINT; - - private KeyguardManager mKeyguardManager; - private FingerprintManager.CryptoObject mCryptoObject; - private FingerprintUiHelper mFingerprintUiHelper; - FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder; - - boolean disableBackup; - - public FingerprintAuthenticationDialogFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Do not create a new Fragment when the Activity is re-created such as orientation changes. - setRetainInstance(true); - setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog); - - mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); - mFingerprintUiHelperBuilder = new FingerprintUiHelper.FingerprintUiHelperBuilder( - getContext(), getContext().getSystemService(FingerprintManager.class)); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - Bundle args = getArguments(); - disableBackup = args.getBoolean("disableBackup"); - Log.d(TAG, "disableBackup: " + disableBackup); - - int fingerprint_auth_dialog_title_id = getResources() - .getIdentifier("fingerprint_auth_dialog_title", "string", - Fingerprint.packageName); - getDialog().setTitle(getString(fingerprint_auth_dialog_title_id)); - int fingerprint_dialog_container_id = getResources() - .getIdentifier("fingerprint_dialog_container", "layout", - Fingerprint.packageName); - View v = inflater.inflate(fingerprint_dialog_container_id, container, false); - int cancel_button_id = getResources() - .getIdentifier("cancel_button", "id", Fingerprint.packageName); - mCancelButton = (Button) v.findViewById(cancel_button_id); - mCancelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Fingerprint.onCancelled(); - dismissAllowingStateLoss(); - } - }); - - int second_dialog_button_id = getResources() - .getIdentifier("second_dialog_button", "id", Fingerprint.packageName); - mSecondDialogButton = (Button) v.findViewById(second_dialog_button_id); - if (disableBackup) { - mSecondDialogButton.setVisibility(View.GONE); - } - mSecondDialogButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - goToBackup(); - } - }); - int fingerprint_container_id = getResources() - .getIdentifier("fingerprint_container", "id", Fingerprint.packageName); - mFingerprintContent = v.findViewById(fingerprint_container_id); - - int new_fingerprint_enrolled_description_id = getResources() - .getIdentifier("new_fingerprint_enrolled_description", "id", - Fingerprint.packageName); - - int fingerprint_icon_id = getResources() - .getIdentifier("fingerprint_icon", "id", Fingerprint.packageName); - int fingerprint_status_id = getResources() - .getIdentifier("fingerprint_status", "id", Fingerprint.packageName); - mFingerprintUiHelper = mFingerprintUiHelperBuilder.build( - (ImageView) v.findViewById(fingerprint_icon_id), - (TextView) v.findViewById(fingerprint_status_id), this); - updateStage(); - - // If fingerprint authentication is not available, switch immediately to the backup - // (password) screen. - if (!mFingerprintUiHelper.isFingerprintAuthAvailable()) { - goToBackup(); - } - return v; - } - - - @Override - public void onResume() { - super.onResume(); - if (mStage == Stage.FINGERPRINT) { - mFingerprintUiHelper.startListening(mCryptoObject); - } - } - - public void setStage(Stage stage) { - mStage = stage; - } - - @Override - public void onPause() { - super.onPause(); - mFingerprintUiHelper.stopListening(); - } - - /** - * Sets the crypto object to be passed in when authenticating with fingerprint. - */ - public void setCryptoObject(FingerprintManager.CryptoObject cryptoObject) { - mCryptoObject = cryptoObject; - } - - /** - * Switches to backup (password) screen. This either can happen when fingerprint is not - * available or the user chooses to use the password authentication method by pressing the - * button. This can also happen when the user had too many fingerprint attempts. - */ - private void goToBackup() { - if(disableBackup) - { - Fingerprint.onCancelled(); - dismissAllowingStateLoss(); - } - else{ - mStage = Stage.BACKUP; - updateStage(); - } - } - - private void updateStage() { - int cancel_id = getResources() - .getIdentifier("fingerprint_cancel", "string", Fingerprint.packageName); - switch (mStage) { - case FINGERPRINT: - mCancelButton.setText(cancel_id); - int use_backup_id = getResources() - .getIdentifier("fingerprint_use_backup", "string", Fingerprint.packageName); - mSecondDialogButton.setText(use_backup_id); - mFingerprintContent.setVisibility(View.VISIBLE); - break; - case NEW_FINGERPRINT_ENROLLED: - // Intentional fall through - case BACKUP: - if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) { - - } - if (!mKeyguardManager.isKeyguardSecure()) { - // Show a message that the user hasn't set up a lock screen. - int secure_lock_screen_required_id = getResources() - .getIdentifier("secure_lock_screen_required", "string", - Fingerprint.packageName); - Toast.makeText(getContext(), - getString(secure_lock_screen_required_id), - Toast.LENGTH_LONG).show(); - return; - } - showAuthenticationScreen(); - break; - } - } - - private void showAuthenticationScreen() { - // Create the Confirm Credentials screen. You can customize the title and description. Or - // we will provide a generic one for you if you leave it null - Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); - if (intent != null) { - startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { - // Challenge completed, proceed with using cipher - if (resultCode == getActivity().RESULT_OK) { - Fingerprint.onAuthenticated(false /* used backup */); - } else { - // The user canceled or didn’t complete the lock screen - // operation. Go to error/cancellation flow. - Fingerprint.onCancelled(); - } - dismissAllowingStateLoss(); - } - } - - @Override - public void onAuthenticated() { - // Callback from FingerprintUiHelper. Let the activity know that authentication was - // successful. - Fingerprint.onAuthenticated(true /* withFingerprint */); - dismissAllowingStateLoss(); - } - - @Override - public void onError() { - if(this.getActivity() != null) - goToBackup(); - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - Fingerprint.onCancelled(); - } - - /** - * Enumeration to indicate which authentication method the user is trying to authenticate with. - */ - public enum Stage { - FINGERPRINT, - NEW_FINGERPRINT_ENROLLED, - BACKUP - } -} diff --git a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintUiHelper.java b/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintUiHelper.java deleted file mode 100644 index e1402c5..0000000 --- a/platforms/android/app/src/main/java/de/niklasmerz/cordova/fingerprint/FingerprintUiHelper.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * Modifications copyright (C) 2016 Niklas Merz - * - * 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 - * - * http://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 - */ - -package de.niklasmerz.cordova.fingerprint; - -import android.annotation.TargetApi; -import android.content.Context; -import android.hardware.fingerprint.FingerprintManager; -import android.os.CancellationSignal; -import android.widget.ImageView; -import android.widget.TextView; - - -/** - * Small helper class to manage text/icon around fingerprint authentication UI. - */ -@TargetApi(23) -public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback { - - static final long ERROR_TIMEOUT_MILLIS = 1600; - static final long SUCCESS_DELAY_MILLIS = 1300; - - private final Context mContext; - private final FingerprintManager mFingerprintManager; - private final ImageView mIcon; - private final TextView mErrorTextView; - private final Callback mCallback; - private CancellationSignal mCancellationSignal; - - boolean mSelfCancelled; - - /** - * Builder class for {@link FingerprintUiHelper} in which injected fields from Dagger - * holds its fields and takes other arguments in the {@link #build} method. - */ - public static class FingerprintUiHelperBuilder { - private final FingerprintManager mFingerPrintManager; - private final Context mContext; - - public FingerprintUiHelperBuilder(Context context, FingerprintManager fingerprintManager) { - mFingerPrintManager = fingerprintManager; - mContext = context; - } - - public FingerprintUiHelper build(ImageView icon, TextView errorTextView, Callback callback) { - return new FingerprintUiHelper(mContext, mFingerPrintManager, icon, errorTextView, - callback); - } - } - - /** - * Constructor for {@link FingerprintUiHelper}. This method is expected to be called from - * only the {@link FingerprintUiHelperBuilder} class. - */ - private FingerprintUiHelper(Context context, FingerprintManager fingerprintManager, - ImageView icon, TextView errorTextView, Callback callback) { - mFingerprintManager = fingerprintManager; - mIcon = icon; - mErrorTextView = errorTextView; - mCallback = callback; - mContext = context; - } - - public boolean isFingerprintAuthAvailable() { - return mFingerprintManager.isHardwareDetected() - && mFingerprintManager.hasEnrolledFingerprints(); - } - - public void startListening(FingerprintManager.CryptoObject cryptoObject) { - if (!isFingerprintAuthAvailable()) { - return; - } - mCancellationSignal = new CancellationSignal(); - mSelfCancelled = false; - mFingerprintManager - .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null); - - int ic_fp_40px_id = mContext.getResources() - .getIdentifier("ic_fp_40px", "drawable", Fingerprint.packageName); - mIcon.setImageResource(ic_fp_40px_id); - } - - public void stopListening() { - if (mCancellationSignal != null) { - mSelfCancelled = true; - mCancellationSignal.cancel(); - mCancellationSignal = null; - } - } - - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - if (!mSelfCancelled) { - showError(errString); - mIcon.postDelayed(new Runnable() { - @Override - public void run() { - mCallback.onError(); - } - }, ERROR_TIMEOUT_MILLIS); - } - } - - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - showError(helpString); - } - - @Override - public void onAuthenticationFailed() { - int fingerprint_not_recognized_id = mContext.getResources() - .getIdentifier("fingerprint_not_recognized", "string", Fingerprint.packageName); - showError(mIcon.getResources().getString( - fingerprint_not_recognized_id)); - } - - @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { - mErrorTextView.removeCallbacks(mResetErrorTextRunnable); - int ic_fingerprint_success_id = mContext.getResources() - .getIdentifier("ic_fingerprint_success", "drawable", Fingerprint.packageName); - mIcon.setImageResource(ic_fingerprint_success_id); - int success_color_id = mContext.getResources() - .getIdentifier("success_color", "color", Fingerprint.packageName); - mErrorTextView.setTextColor( - mErrorTextView.getResources().getColor(success_color_id, null)); - int fingerprint_success_id = mContext.getResources() - .getIdentifier("fingerprint_success", "string", Fingerprint.packageName); - mErrorTextView.setText( - mErrorTextView.getResources().getString(fingerprint_success_id)); - mIcon.postDelayed(new Runnable() { - @Override - public void run() { - mCallback.onAuthenticated(); - } - }, SUCCESS_DELAY_MILLIS); - } - - private void showError(CharSequence error) { - int ic_fingerprint_error_id = mContext.getResources() - .getIdentifier("ic_fingerprint_error", "drawable", Fingerprint.packageName); - mIcon.setImageResource(ic_fingerprint_error_id); - mErrorTextView.setText(error); - int warning_color_id = mContext.getResources() - .getIdentifier("warning_color", "color", Fingerprint.packageName); - mErrorTextView.setTextColor( - mErrorTextView.getResources().getColor(warning_color_id, null)); - mErrorTextView.removeCallbacks(mResetErrorTextRunnable); - mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS); - } - - Runnable mResetErrorTextRunnable = new Runnable() { - @Override - public void run() { - int hint_color_id = mContext.getResources() - .getIdentifier("hint_color", "color", Fingerprint.packageName); - mErrorTextView.setTextColor( - mErrorTextView.getResources().getColor(hint_color_id, null)); - int fingerprint_hint_id = mContext.getResources() - .getIdentifier("fingerprint_hint", "string", Fingerprint.packageName); - mErrorTextView.setText( - mErrorTextView.getResources().getString(fingerprint_hint_id)); - int ic_fp_40px_id = mContext.getResources() - .getIdentifier("ic_fp_40px", "drawable", Fingerprint.packageName); - mIcon.setImageResource(ic_fp_40px_id); - } - }; - - public interface Callback { - - void onAuthenticated(); - - void onError(); - } -} diff --git a/platforms/android/app/src/main/res/drawable-hdpi/ic_fp_40px.png b/platforms/android/app/src/main/res/drawable-hdpi/ic_fp_40px.png deleted file mode 100644 index 48ebd8a..0000000 Binary files a/platforms/android/app/src/main/res/drawable-hdpi/ic_fp_40px.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-hdpi/icon.png b/platforms/android/app/src/main/res/drawable-hdpi/icon.png index 1769fc5..a79c7cf 100644 Binary files a/platforms/android/app/src/main/res/drawable-hdpi/icon.png and b/platforms/android/app/src/main/res/drawable-hdpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable-ldpi/icon.png b/platforms/android/app/src/main/res/drawable-ldpi/icon.png index 1769fc5..24166c6 100644 Binary files a/platforms/android/app/src/main/res/drawable-ldpi/icon.png and b/platforms/android/app/src/main/res/drawable-ldpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable-mdpi/ic_fp_40px.png b/platforms/android/app/src/main/res/drawable-mdpi/ic_fp_40px.png deleted file mode 100644 index 122f442..0000000 Binary files a/platforms/android/app/src/main/res/drawable-mdpi/ic_fp_40px.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-mdpi/icon.png b/platforms/android/app/src/main/res/drawable-mdpi/icon.png index 92a3334..613d8e7 100644 Binary files a/platforms/android/app/src/main/res/drawable-mdpi/icon.png and b/platforms/android/app/src/main/res/drawable-mdpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable-nodpi/android_robot.png b/platforms/android/app/src/main/res/drawable-nodpi/android_robot.png deleted file mode 100644 index 40bf934..0000000 Binary files a/platforms/android/app/src/main/res/drawable-nodpi/android_robot.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-xhdpi/ic_fp_40px.png b/platforms/android/app/src/main/res/drawable-xhdpi/ic_fp_40px.png deleted file mode 100644 index e1c9590..0000000 Binary files a/platforms/android/app/src/main/res/drawable-xhdpi/ic_fp_40px.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-xhdpi/icon.png b/platforms/android/app/src/main/res/drawable-xhdpi/icon.png index 4f54d23..1737ab8 100644 Binary files a/platforms/android/app/src/main/res/drawable-xhdpi/icon.png and b/platforms/android/app/src/main/res/drawable-xhdpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png b/platforms/android/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png deleted file mode 100644 index f7e8724..0000000 Binary files a/platforms/android/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-xxhdpi/icon.png b/platforms/android/app/src/main/res/drawable-xxhdpi/icon.png index ea73508..a8c7b45 100644 Binary files a/platforms/android/app/src/main/res/drawable-xxhdpi/icon.png and b/platforms/android/app/src/main/res/drawable-xxhdpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png b/platforms/android/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png deleted file mode 100644 index 0fb8545..0000000 Binary files a/platforms/android/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png and /dev/null differ diff --git a/platforms/android/app/src/main/res/drawable-xxxhdpi/icon.png b/platforms/android/app/src/main/res/drawable-xxxhdpi/icon.png index 8b754c8..5b33488 100644 Binary files a/platforms/android/app/src/main/res/drawable-xxxhdpi/icon.png and b/platforms/android/app/src/main/res/drawable-xxxhdpi/icon.png differ diff --git a/platforms/android/app/src/main/res/drawable/ic_fingerprint_error.xml b/platforms/android/app/src/main/res/drawable/ic_fingerprint_error.xml deleted file mode 100644 index be46116..0000000 --- a/platforms/android/app/src/main/res/drawable/ic_fingerprint_error.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/platforms/android/app/src/main/res/drawable/ic_fingerprint_success.xml b/platforms/android/app/src/main/res/drawable/ic_fingerprint_success.xml deleted file mode 100644 index 261f3e7..0000000 --- a/platforms/android/app/src/main/res/drawable/ic_fingerprint_success.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/platforms/android/app/src/main/res/layout/biometric_activity.xml b/platforms/android/app/src/main/res/layout/biometric_activity.xml new file mode 100644 index 0000000..5708761 --- /dev/null +++ b/platforms/android/app/src/main/res/layout/biometric_activity.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/platforms/android/app/src/main/res/layout/fingerprint_dialog_container.xml b/platforms/android/app/src/main/res/layout/fingerprint_dialog_container.xml deleted file mode 100644 index 0c6face..0000000 --- a/platforms/android/app/src/main/res/layout/fingerprint_dialog_container.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - -
    +
    + + 《市民卡免密代扣签约协议》 +
    -
    验证
    +
    签约
    diff --git a/www/js/bindcard.js b/www/js/bindcard.js index f7e91a2..7af7d38 100644 --- a/www/js/bindcard.js +++ b/www/js/bindcard.js @@ -81,9 +81,15 @@ var app = { window.localStorage.setItem("idno", idno); window.localStorage.setItem("phoneX",ret.phonex); window.localStorage.setItem("personid", ret.personid); + window.localStorage.setItem("userid", ret.personid); window.localStorage.setItem("signed", ret.signed); window.localStorage.setItem("paypwdset", ret.paypwdset); - window.location = "bindcheck.html"; + if(ret.signed=='yes'){ + window.location = "main.html" + }else{ + window.location = "bindcheck.html"; + } + } else { if (ret.code == -1) { $.alert(ret.msg, "提示", function() { diff --git a/www/js/bindcheck.js b/www/js/bindcheck.js index 32dba4c..8862e73 100644 --- a/www/js/bindcheck.js +++ b/www/js/bindcheck.js @@ -18,19 +18,10 @@ var app = { btnTime('codebtn'); }, getCode: function() { - var name = window.localStorage.getItem("name"); - var cardno = window.localStorage.getItem("cardno"); - var idtype = window.localStorage.getItem("idtype"); - var idno = window.localStorage.getItem("idno"); - $.showLoading("正在请求"); var param = { - "cardno": cardno, - "idtype": idtype, - "name": name, - "idno": idno } - V1Bindcard(param, function(ok, ret) { + V1Bindcardcode(param, function(ok, ret) { if (ok) { $.hideLoading(); if (ret.code == 200) { @@ -57,37 +48,27 @@ var app = { if(isEmpty(code)){ return; } - var personid = window.localStorage.getItem("personid"); + var agree = $("input[type=checkbox]:checked").val(); + if(isEmpty(agree)){ + $.alert("请同意市民卡免密代扣签约协议", "提示"); + return; + } $.showLoading("正在处理"); var param={ - "personid":personid, - "code":code, + "agree":code } - V1Checkcode(param,function(ok,ret){ - if(ok){ - $.hideLoading(); - console.log(ret) + V1Signbxy(param,function(ok, ret, err) { + if (ok) { + $.hideLoading(); if(ret.code==200){ - window.localStorage.setItem("paypwdtype", "new"); - window.localStorage.setItem("userid", personid); - var signed = window.localStorage.getItem("signed"); - var paypwdset = window.localStorage.getItem("paypwdset"); - console.log(paypwdset) - if (!paypwdset||'false'==paypwdset) { - window.location = "paypwdset.html"; - } else { - if (!isEmpty(signed) && signed == 'yes') { - window.location = 'main.html' - } else { - window.location = 'signxy.html' - } - } + window.localStorage.setItem("signed",ret.signed); + window.location="main.html"; }else{ - $.alert(ret.msg, "错误"); - } - }else{ + $.alert(ret.msg, "错误"); + } + } else { $.hideLoading(); - $.alert("请求失败了"+ret.status+",请稍后再试", "错误"); + $.alert("请求失败了:" + ret.status+"请稍后再试", "错误"); } }) } diff --git a/www/js/login.js b/www/js/login.js index 7931a48..88950f3 100644 --- a/www/js/login.js +++ b/www/js/login.js @@ -7,6 +7,29 @@ var app = { onDeviceReady: function() { this.receivedEvent('deviceready'); + this.loginWithFinger(); + }, + loginWithFinger:function(){ + let loginfinger = window.localStorage.getItem("loginfinger"); + let phone = window.localStorage.getItem("phone"); + let pwd = window.localStorage.getItem("localpwd"); + let _this = this; + if(phone&&pwd&&loginfinger&&loginfinger=='1'){ + Fingerprint.isAvailable(function(result){ + Fingerprint.show({ + title:"正在使用指纹或人脸登录", + description: "请验证您的指纹或人脸" + }, function(success){ + _this.loginIt(phone,pwd); + }, function(error){ + $.alert("验证失败,指纹或人脸错误,请使用密码登录", "提示"); + //$("#login").removeAttr("checked"); + }); + + }, function(message){ + $.alert("设备不支持指纹或人脸,请使用密码登录", "提示"); + }); + } }, // Update DOM on a Received Event @@ -29,7 +52,9 @@ var app = { if(isEmpty(phone)||isEmpty(pwd)){ return; } - //loadingElement('loginBtn', '登录中...') + this.loginIt(phone,pwd); + }, + loginIt: function(phone,pwd){ $.showLoading("登录中"); var param={ "username":phone, @@ -56,6 +81,7 @@ var app = { window.localStorage.setItem("signed",ret.signed); window.localStorage.setItem("paypwdset",ret.paypwdset); window.localStorage.setItem("name",ret.name); + window.localStorage.setItem("localpwd",pwd); window.location = "main.html"; } }else{ diff --git a/www/js/main.js b/www/js/main.js index bc93d91..78b896f 100644 --- a/www/js/main.js +++ b/www/js/main.js @@ -111,6 +111,25 @@ var app = { }) }) }); + $('#accsignbtn').click(function() { + app.checkBefore(function() { + var signed = window.localStorage.getItem("signed"); + var cum = new auiDialog({}); + if (isEmpty(signed) || signed != 'yes') { + var confirm = cum.alert({ + title: "提示", + msg: '您尚未签约市民卡免密代扣签约协议,无法使用该功能', + buttons: ['取消', '去签约'] + }, function(ret) { + if (ret.buttonIndex == 2) { + app.toSignCode(); + } + }) + }else{ + window.location = 'signxycheck.html' + } + }) + }); this.initData(); this.backBtn(); }, @@ -153,13 +172,15 @@ var app = { $("#loaddata").show() $("#nodata").hide(); var param = { - "pageno": 1 + "pageno": 1, + "platform":device.platform } V1Bills(param, function(ok, ret) { if (ok) { - //console.log(ret) + console.log(ret) if (ret.code == 200) { - app.checkVersion(ret.version,ret.minversion,ret.versionmsg); + app.checkVersion(ret.version,ret.minversion,ret.versionmsg,ret.versionurl); + //app.checkVersion("1.3.9","1","new func","https://shouji.baidu.com/software/26706357.html"); $("#maingt").text(ret.t + "!") $("#user-amount").text(ret.amount) $("#user-point").text(ret.point) @@ -196,14 +217,21 @@ var app = { } }) }, - checkVersion:function(ver,minver,msg){ + checkVersion:function(ver,minver,m,url){ cordova.getAppVersion.getVersionNumber(function (version) { //alert(version); - if(versionversion){ - alert("有新的版本"); + //alert(version); + if(ver>version){ + var cum = new auiDialog({}); + var confirm = cum.alert({ + title: "有新的版本", + msg: m, + buttons: ['取消', '去更新'] + }, function(ret) { + if (ret.buttonIndex == 2) { + cordova.InAppBrowser.open(url, '_system', 'location=no,toolbar=yes,toolbarposition=top,closebuttoncaption=关闭'); + } + }) } }); }, @@ -282,38 +310,40 @@ var app = { } }, checkOther: function(callback) { - var payseted = window.localStorage.getItem("paypwdset"); var signed = window.localStorage.getItem("signed"); var cum = new auiDialog({}); - console.log(payseted, signed) - if (isEmpty(payseted) || !payseted || payseted != 'true') { + + if (isEmpty(signed) || signed != 'yes') { var confirm = cum.alert({ title: "提示", - msg: '您还没有设置支付密码,无法使用该功能', - buttons: ['取消', '去设置'] + msg: '您尚未签约市民卡免密代扣签约协议,无法使用该功能', + buttons: ['取消', '去签约'] }, function(ret) { if (ret.buttonIndex == 2) { - window.location = 'paypwdset.html' + app.toSignCode(); } }) } else { - if (isEmpty(signed) || signed != 'yes') { - var confirm = cum.alert({ - title: "提示", - msg: '您尚未签约代扣免密付协议,无法使用该功能', - buttons: ['取消', '去签约'] - }, function(ret) { - if (ret.buttonIndex == 2) { - window.location = 'signxy.html' - } - }) - } else { - if (callback) { - callback() - } + if (callback) { + callback() } } }, + toSignCode: function(){ + var param = { + } + V1Bindcardcode(param, function(ok, ret) { + if (ok) { + if (ret.code == 200) { + window.location = 'bindcheck.html' + } else { + $.alert(ret.msg, "错误"); + } + } else { + $.alert("请求失败了 " + ret.status + ",请稍后再试", "错误"); + } + }) + }, toSign: function() { window.location = 'signxycheck.html' }, diff --git a/www/js/qrcode.js b/www/js/qrcode.js index b9d81b9..51b2aa1 100644 --- a/www/js/qrcode.js +++ b/www/js/qrcode.js @@ -22,6 +22,10 @@ var app = { console.log(err); }); } + document.addEventListener("backbutton", function(e){ + e.preventDefault(); + app.backTo(); + }, false); }, loadQrcode: function() { $.showLoading("加载中"); @@ -62,6 +66,7 @@ var app = { if (ok) { if (ret.code == 200) { $.hideLoading(); + $("#qrcode").html(''); var qrcode = new QRCode(document.getElementById("qrcode"), { text: ret.qrcode, width: 200, diff --git a/www/js/security.js b/www/js/security.js index dd5475d..d159a40 100644 --- a/www/js/security.js +++ b/www/js/security.js @@ -5,23 +5,68 @@ var app = { document.addEventListener('deviceready', this.onDeviceReady.bind(this), false); }, onDeviceReady: function() { + let loginfinger = window.localStorage.getItem("loginfinger"); + if(loginfinger&&loginfinger=='1'){ + $("#login").prop("checked","checked"); + } }, editPwd: function() { window.location = "editpwd.html"; }, toPayPwd: function() { - window.location = "paypwdmng.html"; + var payseted = window.localStorage.getItem("paypwdset"); + if (isEmpty(payseted) || !payseted || payseted != 'true') { + window.location = 'paypwdset.html' + } else{ + window.location = "paypwdmng.html"; + } }, logout:function(){ window.localStorage.removeItem("token"); window.location = "login.html"; }, loginByTouchId: function() { - Fingerprint.isAvailable(function(result){ - $.alert("设备不支持指纹", "提示"); - }, function(message){ - alert(message); - }); + let loginfinger = window.localStorage.getItem("loginfinger"); + if(loginfinger&&loginfinger=='1'){ + var confirm = new auiDialog({}).alert({ + title: "提示", + msg: '确定关闭指纹或人脸登录吗?', + buttons: ['取消', '确定'] + }, function(ret) { + if (ret.buttonIndex == 2) { + window.localStorage.setItem("loginfinger","0"); + $("#login").removeAttr("checked"); + }else{ + //$("#login").attr("checked","checked"); + } + }); + }else{ + let pwd = window.localStorage.getItem("localpwd"); + if(!pwd){ + $.alert("长时间未登录,需要先登录后再使用该功能", "提示",function(){ + window.localStorage.removeItem("token"); + window.location = "login.html"; + }); + return; + } + $("#login").removeAttr("checked"); + Fingerprint.isAvailable(function(result){ + Fingerprint.show({ + title:"大理市民卡开启指纹或人脸登录", + description: "请验证您的指纹或人脸" + }, function(success){ + window.localStorage.setItem("loginfinger","1"); + $.alert("验证成功,您已开启指纹或人脸登录", "提示"); + $("#login").prop("checked","checked"); + }, function(error){ + $.alert("验证失败,指纹或人脸错误次数过多,请稍后再试", "提示"); + //$("#login").removeAttr("checked"); + }); + + }, function(message){ + $.alert("设备不支持指纹或人脸", "提示"); + }); + } } }; app.initialize(); diff --git a/www/js/server.js b/www/js/server.js index 3e8ae05..426bf2d 100644 --- a/www/js/server.js +++ b/www/js/server.js @@ -1,4 +1,4 @@ -var dev = true; +var dev = false; var SERVER = "https://yy.dlsmk.cn/payapi/mobileapi"; var DOOR_URl = "https://yy.dlsmk.cn/door/app/appMintUIindex";//门禁服务地址 var GLOBAL_TODAY=""; @@ -7,6 +7,10 @@ var CURRENT_INDEX=1; if (dev) { SERVER = "http://172.28.43.7:8099/payapi/mobileapi"; } +function V1Bindcardcode(param,callback) { + ajaxPost("/v1/bindcardcode", param, callback) +} + function V1Unbindcard(param,callback){ ajaxPost("/v1/unbindcard", param, callback) } diff --git a/www/js/signxy.js b/www/js/signxy.js index f97f3d4..4ee8598 100644 --- a/www/js/signxy.js +++ b/www/js/signxy.js @@ -14,10 +14,10 @@ var app = { if(ret.code==200){ $("#content").html(ret.page); if(isEmpty(ret.signed)||ret.signed!='yes'){ - $("#btn").show(); - $("#content").css("bottom","135px"); + //$("#btn").show(); + //$("#content").css("bottom","135px"); }else{ - $("#content").css("bottom","10px"); + //$("#content").css("bottom","10px"); } }else{ $.alert(ret.msg, "错误"); diff --git a/www/js/unsignedcheck.js b/www/js/unsignedcheck.js new file mode 100644 index 0000000..ce72038 --- /dev/null +++ b/www/js/unsignedcheck.js @@ -0,0 +1,71 @@ +var dict; +var app = { + + // Application Constructor + initialize: function() { + document.addEventListener('deviceready', this.onDeviceReady.bind(this), false); + }, + onDeviceReady: function() { + var phone = window.localStorage.getItem("phoneX"); + if(!isEmpty(phone)){ + $("#phone").text(phone) + }else{ + phone = window.localStorage.getItem("phone"); + $("#phone").text(phone) + } + app.getCode(); + }, + getCode: function() { + $.showLoading("正在请求"); + var param = { + } + V1Bindcardcode(param, function(ok, ret) { + if (ok) { + $.hideLoading(); + if (ret.code == 200) { + $("#codebtn").attr("disabled","disabled") + $("#codebtn").addClass("vcodedisabled") + btnTime('codebtn'); + } else { + if (ret.code == -1) { + $.alert(ret.msg, "提示", function() { + window.location = "main.html" + }); + } else { + $.alert(ret.msg, "错误"); + } + } + } else { + $.hideLoading(); + $.alert("请求失败了 " + ret.status + ",请稍后再试", "错误"); + } + }) + }, + doNext: function() { + var code = $("#code").val(); + if(isEmpty(code)){ + return; + } + $.showLoading("正在处理"); + var param={ + "code":code + } + V1UnSignbxy(param, function(ok, ret, err) { + if (ok) { + $.hideLoading(); + if (ret.code == 200) { + window.localStorage.setItem("signed", ret.signed); + $.alert('解除成功', "提示", function() { + window.location = 'main.html' + }); + } else { + $.alert(ret.msg, "错误"); + } + } else { + $.hideLoading(); + $.alert("请求失败了:" + ret.status + "请稍后再试", "错误"); + } + }) + } +}; +app.initialize(); \ No newline at end of file diff --git a/www/main.html b/www/main.html index b357ca3..c8bb590 100644 --- a/www/main.html +++ b/www/main.html @@ -222,7 +222,7 @@
    -
  • +
  • diff --git a/www/qrcode.html b/www/qrcode.html index 5ce5788..a64f153 100644 --- a/www/qrcode.html +++ b/www/qrcode.html @@ -24,9 +24,9 @@

    请将二维码对准扫描设备

    - +
    +
    刷新二维码
    +
    diff --git a/www/security.html b/www/security.html index b205cdb..971cc59 100644 --- a/www/security.html +++ b/www/security.html @@ -33,13 +33,17 @@
  • - +
    指纹\人脸登录
    -
    +
  • + + +
    +
    • diff --git a/www/signxy.html b/www/signxy.html index cb0f1ba..c22eda3 100644 --- a/www/signxy.html +++ b/www/signxy.html @@ -11,31 +11,19 @@ - 签约代扣免密支付协议 + 市民卡免密代扣签约协议
      - + -
      签约代扣免密支付协议
      +
      市民卡免密代扣签约协议
      -
      +
      - + diff --git a/www/unsignedcheck.html b/www/unsignedcheck.html new file mode 100644 index 0000000..f677648 --- /dev/null +++ b/www/unsignedcheck.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + 解约 + + +
      + + + +
      解约
      +
      +
      +
      已发送验证码到您的手机:
      +
      +
      +
      + +
      +
      + +
      +
      + +
      +
      +
      +
      +
      解约
      +
      +
      + + + + + + + \ No newline at end of file