Repository: friimaind/pi-hole-droid Branch: master Commit: fd06eda5b96d Files: 422 Total size: 5.3 MB Directory structure: gitextract_97sv444t/ ├── .gitignore ├── LICENSE ├── PRIVACY.MD ├── README.md ├── config.xml ├── hooks/ │ └── README.md ├── platforms/ │ ├── android/ │ │ ├── .gitignore │ │ ├── AndroidManifest.xml │ │ ├── CordovaLib/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── build/ │ │ │ │ ├── generated/ │ │ │ │ │ └── source/ │ │ │ │ │ └── buildConfig/ │ │ │ │ │ ├── debug/ │ │ │ │ │ │ └── org/ │ │ │ │ │ │ └── apache/ │ │ │ │ │ │ └── cordova/ │ │ │ │ │ │ └── BuildConfig.java │ │ │ │ │ └── release/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── cordova/ │ │ │ │ │ └── BuildConfig.java │ │ │ │ ├── intermediates/ │ │ │ │ │ ├── bundles/ │ │ │ │ │ │ ├── debug/ │ │ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ │ │ └── classes.jar │ │ │ │ │ │ └── release/ │ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ │ └── classes.jar │ │ │ │ │ ├── incremental/ │ │ │ │ │ │ ├── compileDebugAidl/ │ │ │ │ │ │ │ └── dependency.store │ │ │ │ │ │ ├── compileReleaseAidl/ │ │ │ │ │ │ │ └── dependency.store │ │ │ │ │ │ ├── mergeDebugAssets/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── mergeDebugJniLibFolders/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── mergeDebugShaders/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── mergeReleaseAssets/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── mergeReleaseJniLibFolders/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── mergeReleaseShaders/ │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ ├── packageDebugResources/ │ │ │ │ │ │ │ ├── compile-file-map.properties │ │ │ │ │ │ │ └── merger.xml │ │ │ │ │ │ └── packageReleaseResources/ │ │ │ │ │ │ ├── compile-file-map.properties │ │ │ │ │ │ └── merger.xml │ │ │ │ │ ├── incremental-safeguard/ │ │ │ │ │ │ ├── debug/ │ │ │ │ │ │ │ └── tag.txt │ │ │ │ │ │ └── release/ │ │ │ │ │ │ └── tag.txt │ │ │ │ │ └── manifests/ │ │ │ │ │ └── aapt/ │ │ │ │ │ ├── debug/ │ │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ │ └── release/ │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ └── outputs/ │ │ │ │ └── aar/ │ │ │ │ ├── CordovaLib-debug.aar │ │ │ │ └── CordovaLib-release.aar │ │ │ ├── build.gradle │ │ │ ├── cordova.gradle │ │ │ ├── project.properties │ │ │ └── src/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── cordova/ │ │ │ ├── AuthenticationToken.java │ │ │ ├── CallbackContext.java │ │ │ ├── CallbackMap.java │ │ │ ├── Config.java │ │ │ ├── ConfigXmlParser.java │ │ │ ├── CordovaActivity.java │ │ │ ├── CordovaArgs.java │ │ │ ├── CordovaBridge.java │ │ │ ├── CordovaClientCertRequest.java │ │ │ ├── CordovaDialogsHelper.java │ │ │ ├── CordovaHttpAuthHandler.java │ │ │ ├── CordovaInterface.java │ │ │ ├── CordovaInterfaceImpl.java │ │ │ ├── CordovaPlugin.java │ │ │ ├── CordovaPreferences.java │ │ │ ├── CordovaResourceApi.java │ │ │ ├── CordovaWebView.java │ │ │ ├── CordovaWebViewEngine.java │ │ │ ├── CordovaWebViewImpl.java │ │ │ ├── CoreAndroid.java │ │ │ ├── ExposedJsApi.java │ │ │ ├── ICordovaClientCertRequest.java │ │ │ ├── ICordovaCookieManager.java │ │ │ ├── ICordovaHttpAuthHandler.java │ │ │ ├── LOG.java │ │ │ ├── NativeToJsMessageQueue.java │ │ │ ├── PluginEntry.java │ │ │ ├── PluginManager.java │ │ │ ├── PluginResult.java │ │ │ ├── ResumeCallback.java │ │ │ ├── Whitelist.java │ │ │ └── engine/ │ │ │ ├── SystemCookieManager.java │ │ │ ├── SystemExposedJsApi.java │ │ │ ├── SystemWebChromeClient.java │ │ │ ├── SystemWebView.java │ │ │ ├── SystemWebViewClient.java │ │ │ └── SystemWebViewEngine.java │ │ ├── android.json │ │ ├── assets/ │ │ │ └── www/ │ │ │ ├── cordova-js-src/ │ │ │ │ ├── android/ │ │ │ │ │ ├── nativeapiprovider.js │ │ │ │ │ └── promptbasednativeapi.js │ │ │ │ ├── exec.js │ │ │ │ ├── platform.js │ │ │ │ └── plugin/ │ │ │ │ └── android/ │ │ │ │ └── app.js │ │ │ ├── cordova.js │ │ │ ├── cordova_plugins.js │ │ │ ├── css/ │ │ │ │ ├── responsive.css │ │ │ │ └── styles.css │ │ │ ├── index.html │ │ │ ├── js/ │ │ │ │ ├── main.js │ │ │ │ └── utilities.js │ │ │ ├── lib/ │ │ │ │ └── DataTables/ │ │ │ │ ├── DataTables-1.10.13/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ ├── dataTables.bootstrap.css │ │ │ │ │ │ ├── dataTables.foundation.css │ │ │ │ │ │ ├── dataTables.jqueryui.css │ │ │ │ │ │ ├── dataTables.semanticui.css │ │ │ │ │ │ ├── jquery.dataTables.css │ │ │ │ │ │ └── jquery.dataTables_themeroller.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── dataTables.bootstrap.js │ │ │ │ │ ├── dataTables.foundation.js │ │ │ │ │ ├── dataTables.jqueryui.js │ │ │ │ │ ├── dataTables.semanticui.js │ │ │ │ │ └── jquery.dataTables.js │ │ │ │ ├── datatables.css │ │ │ │ └── datatables.js │ │ │ ├── partial/ │ │ │ │ ├── about-help.html │ │ │ │ ├── app-settings.html │ │ │ │ ├── dashboard.html │ │ │ │ └── query-log.html │ │ │ └── plugins/ │ │ │ ├── cordova-plugin-splashscreen/ │ │ │ │ └── www/ │ │ │ │ └── splashscreen.js │ │ │ └── phonegap-plugin-barcodescanner/ │ │ │ └── www/ │ │ │ └── barcodescanner.js │ │ ├── build.gradle │ │ ├── cordova/ │ │ │ ├── .jshintrc │ │ │ ├── Api.js │ │ │ ├── android_sdk_version │ │ │ ├── android_sdk_version.bat │ │ │ ├── build │ │ │ ├── build.bat │ │ │ ├── check_reqs │ │ │ ├── check_reqs.bat │ │ │ ├── clean │ │ │ ├── clean.bat │ │ │ ├── defaults.xml │ │ │ ├── lib/ │ │ │ │ ├── Adb.js │ │ │ │ ├── AndroidManifest.js │ │ │ │ ├── AndroidProject.js │ │ │ │ ├── AndroidStudio.js │ │ │ │ ├── android_sdk.js │ │ │ │ ├── build.js │ │ │ │ ├── builders/ │ │ │ │ │ ├── AntBuilder.js │ │ │ │ │ ├── GenericBuilder.js │ │ │ │ │ ├── GradleBuilder.js │ │ │ │ │ └── builders.js │ │ │ │ ├── check_reqs.js │ │ │ │ ├── device.js │ │ │ │ ├── emulator.js │ │ │ │ ├── install-device │ │ │ │ ├── install-device.bat │ │ │ │ ├── install-emulator │ │ │ │ ├── install-emulator.bat │ │ │ │ ├── list-devices │ │ │ │ ├── list-devices.bat │ │ │ │ ├── list-emulator-images │ │ │ │ ├── list-emulator-images.bat │ │ │ │ ├── list-started-emulators │ │ │ │ ├── list-started-emulators.bat │ │ │ │ ├── log.js │ │ │ │ ├── plugin-build.gradle │ │ │ │ ├── pluginHandlers.js │ │ │ │ ├── prepare.js │ │ │ │ ├── retry.js │ │ │ │ ├── run.js │ │ │ │ ├── start-emulator │ │ │ │ └── start-emulator.bat │ │ │ ├── log │ │ │ ├── log.bat │ │ │ ├── loggingHelper.js │ │ │ ├── run │ │ │ ├── run.bat │ │ │ ├── version │ │ │ └── version.bat │ │ ├── libs/ │ │ │ └── barcodescanner.aar │ │ ├── phonegap-plugin-barcodescanner/ │ │ │ └── piholedroid-barcodescanner.gradle │ │ ├── platform_www/ │ │ │ ├── cordova-js-src/ │ │ │ │ ├── android/ │ │ │ │ │ ├── nativeapiprovider.js │ │ │ │ │ └── promptbasednativeapi.js │ │ │ │ ├── exec.js │ │ │ │ ├── platform.js │ │ │ │ └── plugin/ │ │ │ │ └── android/ │ │ │ │ └── app.js │ │ │ ├── cordova.js │ │ │ ├── cordova_plugins.js │ │ │ └── plugins/ │ │ │ ├── cordova-plugin-splashscreen/ │ │ │ │ └── www/ │ │ │ │ └── splashscreen.js │ │ │ └── phonegap-plugin-barcodescanner/ │ │ │ └── www/ │ │ │ └── barcodescanner.js │ │ ├── project.properties │ │ ├── res/ │ │ │ ├── values/ │ │ │ │ └── strings.xml │ │ │ └── xml/ │ │ │ └── config.xml │ │ ├── settings.gradle │ │ ├── src/ │ │ │ ├── com/ │ │ │ │ └── phonegap/ │ │ │ │ └── plugins/ │ │ │ │ └── barcodescanner/ │ │ │ │ └── BarcodeScanner.java │ │ │ ├── friimaind/ │ │ │ │ └── piholedroid/ │ │ │ │ └── MainActivity.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── cordova/ │ │ │ ├── BuildHelper.java │ │ │ ├── PermissionHelper.java │ │ │ ├── splashscreen/ │ │ │ │ └── SplashScreen.java │ │ │ └── whitelist/ │ │ │ └── WhitelistPlugin.java │ │ └── wrapper.gradle │ ├── browser/ │ │ ├── browser.json │ │ ├── config.xml │ │ ├── cordova/ │ │ │ ├── build │ │ │ ├── build.bat │ │ │ ├── clean │ │ │ ├── defaults.xml │ │ │ ├── lib/ │ │ │ │ ├── build.js │ │ │ │ ├── check_reqs.js │ │ │ │ └── clean.js │ │ │ ├── run │ │ │ ├── run.bat │ │ │ ├── version │ │ │ └── version.bat │ │ ├── css/ │ │ │ └── index.css │ │ ├── index.html │ │ ├── js/ │ │ │ └── index.js │ │ ├── manifest.webapp │ │ ├── platform_www/ │ │ │ ├── confighelper.js │ │ │ ├── cordova-js-src/ │ │ │ │ ├── confighelper.js │ │ │ │ ├── exec.js │ │ │ │ └── platform.js │ │ │ ├── cordova.js │ │ │ ├── cordova_plugins.js │ │ │ ├── exec.js │ │ │ ├── platform.js │ │ │ └── plugins/ │ │ │ ├── cordova-plugin-splashscreen/ │ │ │ │ ├── src/ │ │ │ │ │ └── browser/ │ │ │ │ │ └── SplashScreenProxy.js │ │ │ │ └── www/ │ │ │ │ └── splashscreen.js │ │ │ └── phonegap-plugin-barcodescanner/ │ │ │ ├── src/ │ │ │ │ └── browser/ │ │ │ │ └── BarcodeScannerProxy.js │ │ │ └── www/ │ │ │ └── barcodescanner.js │ │ └── www/ │ │ ├── config.xml │ │ ├── confighelper.js │ │ ├── cordova-js-src/ │ │ │ ├── confighelper.js │ │ │ ├── exec.js │ │ │ └── platform.js │ │ ├── cordova.js │ │ ├── cordova_plugins.js │ │ ├── css/ │ │ │ ├── responsive.css │ │ │ └── styles.css │ │ ├── exec.js │ │ ├── index.html │ │ ├── js/ │ │ │ ├── main.js │ │ │ └── utilities.js │ │ ├── lib/ │ │ │ └── DataTables/ │ │ │ ├── DataTables-1.10.13/ │ │ │ │ ├── css/ │ │ │ │ │ ├── dataTables.bootstrap.css │ │ │ │ │ ├── dataTables.foundation.css │ │ │ │ │ ├── dataTables.jqueryui.css │ │ │ │ │ ├── dataTables.semanticui.css │ │ │ │ │ ├── jquery.dataTables.css │ │ │ │ │ └── jquery.dataTables_themeroller.css │ │ │ │ └── js/ │ │ │ │ ├── dataTables.bootstrap.js │ │ │ │ ├── dataTables.foundation.js │ │ │ │ ├── dataTables.jqueryui.js │ │ │ │ ├── dataTables.semanticui.js │ │ │ │ └── jquery.dataTables.js │ │ │ ├── datatables.css │ │ │ └── datatables.js │ │ ├── partial/ │ │ │ ├── about-help.html │ │ │ ├── app-settings.html │ │ │ ├── dashboard.html │ │ │ └── query-log.html │ │ ├── platform.js │ │ └── plugins/ │ │ ├── cordova-plugin-splashscreen/ │ │ │ ├── src/ │ │ │ │ └── browser/ │ │ │ │ └── SplashScreenProxy.js │ │ │ └── www/ │ │ │ └── splashscreen.js │ │ └── phonegap-plugin-barcodescanner/ │ │ ├── src/ │ │ │ └── browser/ │ │ │ └── BarcodeScannerProxy.js │ │ └── www/ │ │ └── barcodescanner.js │ └── platforms.json ├── plugins/ │ ├── android.json │ ├── browser.json │ ├── cordova-plugin-compat/ │ │ ├── README.md │ │ ├── RELEASENOTES.md │ │ ├── package.json │ │ ├── plugin.xml │ │ └── src/ │ │ ├── android/ │ │ │ ├── BuildHelper.java │ │ │ └── PermissionHelper.java │ │ └── cordova-plugin-compat.iml │ ├── cordova-plugin-splashscreen/ │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ ├── RELEASENOTES.md │ │ ├── doc/ │ │ │ ├── de/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── es/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── fr/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── it/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── ja/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── ko/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── pl/ │ │ │ │ ├── README.md │ │ │ │ └── index.md │ │ │ ├── ru/ │ │ │ │ └── index.md │ │ │ └── zh/ │ │ │ ├── README.md │ │ │ └── index.md │ │ ├── package.json │ │ ├── plugin.xml │ │ ├── src/ │ │ │ ├── android/ │ │ │ │ └── SplashScreen.java │ │ │ ├── blackberry10/ │ │ │ │ └── index.js │ │ │ ├── browser/ │ │ │ │ └── SplashScreenProxy.js │ │ │ ├── cordova-plugin-splashscreen.iml │ │ │ ├── ios/ │ │ │ │ ├── CDVSplashScreen.h │ │ │ │ ├── CDVSplashScreen.m │ │ │ │ ├── CDVViewController+SplashScreen.h │ │ │ │ └── CDVViewController+SplashScreen.m │ │ │ ├── tizen/ │ │ │ │ └── SplashScreenProxy.js │ │ │ ├── ubuntu/ │ │ │ │ ├── splashscreen.cpp │ │ │ │ └── splashscreen.h │ │ │ └── wp/ │ │ │ ├── ResolutionHelper.cs │ │ │ └── SplashScreen.cs │ │ ├── tests/ │ │ │ ├── ios/ │ │ │ │ ├── CDVSplashScreenTest/ │ │ │ │ │ ├── .npmignore │ │ │ │ │ ├── CDVSplashScreenLibTests/ │ │ │ │ │ │ ├── ImageNameTest.m │ │ │ │ │ │ ├── ImageNameTestDelegates.h │ │ │ │ │ │ ├── ImageNameTestDelegates.m │ │ │ │ │ │ └── Info.plist │ │ │ │ │ └── CDVSplashScreenTest.xcodeproj/ │ │ │ │ │ ├── project.pbxproj │ │ │ │ │ ├── project.xcworkspace/ │ │ │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ │ └── CDVSplashScreenTest.xccheckout │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ └── xcschemes/ │ │ │ │ │ ├── CDVSplashScreenLib.xcscheme │ │ │ │ │ └── CDVSplashScreenLibTests.xcscheme │ │ │ │ ├── CDVSplashScreenTest.xcworkspace/ │ │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ ├── CDVSplashScreenTest.xccheckout │ │ │ │ │ └── xcschemes/ │ │ │ │ │ └── CordovaLib.xcscheme │ │ │ │ ├── README.md │ │ │ │ ├── doc/ │ │ │ │ │ ├── de/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── es/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── fr/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── it/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── ja/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── ko/ │ │ │ │ │ │ └── README.md │ │ │ │ │ ├── pl/ │ │ │ │ │ │ └── README.md │ │ │ │ │ └── zh/ │ │ │ │ │ └── README.md │ │ │ │ └── package.json │ │ │ ├── plugin.xml │ │ │ └── tests.js │ │ └── www/ │ │ ├── splashscreen.js │ │ └── windows/ │ │ └── SplashScreenProxy.js │ ├── cordova-plugin-whitelist/ │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ ├── RELEASENOTES.md │ │ ├── doc/ │ │ │ ├── de/ │ │ │ │ └── README.md │ │ │ ├── es/ │ │ │ │ └── README.md │ │ │ ├── fr/ │ │ │ │ └── README.md │ │ │ ├── it/ │ │ │ │ └── README.md │ │ │ ├── ja/ │ │ │ │ └── README.md │ │ │ ├── ko/ │ │ │ │ └── README.md │ │ │ ├── pl/ │ │ │ │ └── README.md │ │ │ └── zh/ │ │ │ └── README.md │ │ ├── package.json │ │ ├── plugin.xml │ │ └── src/ │ │ ├── android/ │ │ │ └── WhitelistPlugin.java │ │ └── cordova-plugin-whitelist.iml │ ├── fetch.json │ └── phonegap-plugin-barcodescanner/ │ ├── CHANGELOG.md │ ├── README.md │ ├── hooks/ │ │ └── windows/ │ │ └── check-arch.js │ ├── package.json │ ├── plugin.xml │ ├── spec/ │ │ ├── helper/ │ │ │ └── cordova.js │ │ └── index.spec.js │ ├── src/ │ │ ├── android/ │ │ │ ├── README.md │ │ │ ├── barcodescanner-release-2.1.2.aar │ │ │ ├── barcodescanner.gradle │ │ │ └── com/ │ │ │ └── phonegap/ │ │ │ └── plugins/ │ │ │ └── barcodescanner/ │ │ │ └── BarcodeScanner.java │ │ ├── blackberry10/ │ │ │ ├── LICENSE │ │ │ ├── index.js │ │ │ ├── native/ │ │ │ │ ├── .cproject │ │ │ │ ├── .settings/ │ │ │ │ │ └── com.qnx.tools.ide.core.prefs │ │ │ │ ├── device/ │ │ │ │ │ └── .npmignore │ │ │ │ ├── public/ │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── autolink.h │ │ │ │ │ │ ├── config.h │ │ │ │ │ │ ├── features.h │ │ │ │ │ │ ├── forwards.h │ │ │ │ │ │ ├── json.h │ │ │ │ │ │ ├── reader.h │ │ │ │ │ │ ├── value.h │ │ │ │ │ │ └── writer.h │ │ │ │ │ ├── json_batchallocator.h │ │ │ │ │ ├── json_internalarray.inl │ │ │ │ │ ├── json_internalmap.inl │ │ │ │ │ ├── json_reader.cpp │ │ │ │ │ ├── json_value.cpp │ │ │ │ │ ├── json_valueiterator.inl │ │ │ │ │ ├── json_writer.cpp │ │ │ │ │ ├── plugin.cpp │ │ │ │ │ ├── plugin.h │ │ │ │ │ ├── tokenizer.cpp │ │ │ │ │ └── tokenizer.h │ │ │ │ ├── simulator/ │ │ │ │ │ └── .npmignore │ │ │ │ └── src/ │ │ │ │ ├── Logger.cpp │ │ │ │ ├── Logger.hpp │ │ │ │ ├── barcodescanner_js.cpp │ │ │ │ ├── barcodescanner_js.hpp │ │ │ │ ├── barcodescanner_ndk.cpp │ │ │ │ └── barcodescanner_ndk.hpp │ │ │ └── qrcode.js │ │ ├── browser/ │ │ │ └── BarcodeScannerProxy.js │ │ ├── ios/ │ │ │ ├── CDVBarcodeScanner.bundle/ │ │ │ │ └── beep.caf │ │ │ ├── CDVBarcodeScanner.mm │ │ │ ├── scannerOverlay.xib │ │ │ ├── zxing-all-in-one.cpp │ │ │ └── zxing-all-in-one.h │ │ ├── phonegap-plugin-barcodescanner.iml │ │ ├── windows/ │ │ │ ├── BarcodeScannerProxy.js │ │ │ ├── assets/ │ │ │ │ └── plugin-barcodeScanner.css │ │ │ ├── lib/ │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Reader.cs │ │ │ │ ├── WinRTBarcodeReader.csproj │ │ │ │ └── ZXing.winmd │ │ │ └── lib.UW/ │ │ │ ├── ANY/ │ │ │ │ └── ZXing.winmd │ │ │ ├── ARM/ │ │ │ │ └── ZXing.winmd │ │ │ ├── x64/ │ │ │ │ └── ZXing.winmd │ │ │ └── x86/ │ │ │ └── ZXing.winmd │ │ └── wp8/ │ │ ├── BarcodeScanner.cs │ │ ├── BarcodeScannerTask.cs │ │ ├── BarcodeScannerUI.xaml │ │ └── BarcodeScannerUI.xaml.cs │ ├── tests/ │ │ ├── plugin.xml │ │ └── tests.js │ └── www/ │ └── barcodescanner.js └── www/ ├── css/ │ ├── responsive.css │ └── styles.css ├── index.html ├── js/ │ ├── main.js │ └── utilities.js ├── lib/ │ └── DataTables/ │ ├── DataTables-1.10.13/ │ │ ├── css/ │ │ │ ├── dataTables.bootstrap.css │ │ │ ├── dataTables.foundation.css │ │ │ ├── dataTables.jqueryui.css │ │ │ ├── dataTables.semanticui.css │ │ │ ├── jquery.dataTables.css │ │ │ └── jquery.dataTables_themeroller.css │ │ └── js/ │ │ ├── dataTables.bootstrap.js │ │ ├── dataTables.foundation.js │ │ ├── dataTables.jqueryui.js │ │ ├── dataTables.semanticui.js │ │ └── jquery.dataTables.js │ ├── datatables.css │ └── datatables.js └── partial/ ├── about-help.html ├── app-settings.html ├── dashboard.html └── query-log.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # IDE nbproject/ # Android platforms/android/bin/ platforms/android/gen/ platforms/android/build/ # iOS platforms/ios/build/ platforms/ios/CordovaLib/build/ platforms/ios/www platforms/ios/MyPace/config.xml platforms/ios/.staging # Browser platforms/browser/build/ # Emulator logs platforms/ios/cordova/console.log ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. ================================================ FILE: PRIVACY.MD ================================================ # Pi-hole Droid - Privacy ### Why does Pi-hole Droid request permission to access my camera? The app could scan the QR Code of your Pi-hole to obtain the token to connect to it. This function is available on the "App settings" page. ### Does Pi-hole Droid collect informations? The app will **locally** store the IP and the token of your Pi-hole for the only purpose of connecting to it. ================================================ FILE: README.md ================================================ # Pi-hole Droid ![App Logo](www/assets/mipmap-hdpi/ic_launcher.png?raw=true) Pi-hole Droid is an *unofficial* client that connects to your [Pi-hole](https://github.com/pi-hole/pi-hole) to show charts and statistics. It requires the new *Pi-hole 3.0 FTL* (released on 2 May 2017). Please update your Pi-hole! This is the repository of the Pi-hole Droid's opensource application available on [Google Play Store](https://play.google.com/store/apps/details?id=friimaind.piholedroid). **What is Pi-hole?** > Pi-hole is the multi-platform, network-wide ad blocker which blocks ads for all your devices without the need to install client-side software. Pi-hole Droid makes use of [Pi-hole API](https://github.com/pi-hole/AdminLTE) to obtain all the data. The credentials is **locally stored** on your device through localStorage. ## Download *Please note:* Google Play Store is temporary unavailable, in the meantime you can **get the latest APK** directly from [here](https://github.com/friimaind/pi-hole-droid/releases/latest) or [F-Droid repository](https://f-droid.org/en/packages/friimaind.piholedroid/) ## Screenshots ![Homepage](screenshots/home.png?raw=true "Homepage") ![Menu](screenshots/menu.png?raw=true "Menu") ![Charts](screenshots/charts.png?raw=true "Charts") ![Settings](screenshots/settings.png?raw=true "Settings") ![Toggle On](screenshots/toggle_on.png?raw=true "Toggle On") ![Toggle Off](screenshots/toggle_off.png?raw=true "Toggle Off") ![Home Landscape](screenshots/home-landscape.png?raw=true "Home Landscape") ![Landscape](screenshots/landscape.png?raw=true "Landscape") ![Landscape2](screenshots/landscape-2.png?raw=true "Landscape2") ![Query Log](screenshots/query-log.png?raw=true "Query Log") ## Built With * [Apache Cordova](https://cordova.apache.org/) * [MDL Material Design Lite](https://getmdl.io) * [jQuery](https://jquery.com) * [Chartist JS](https://gionkunz.github.io/chartist-js) * [DataTables](https://datatables.net) * [Hammer.js](http://hammerjs.github.io) * [BarcodeScanner](https://github.com/phonegap/phonegap-plugin-barcodescanner) ## Upcoming releases I'm working on these tasks for the next releases: * Improvements on UI (loading icons on table views, better UI on Query Log page primarily) * Improvements on landscape view (better use of space) * Query Log: highlight blocked queries * Auto update toggle on Query Log's page * Dark theme Do you have a feature request? Please, [write an issue](https://github.com/friimaind/pi-hole-droid/issues). ## Oh no! I found a bug! Please, [write an issue](https://github.com/friimaind/pi-hole-droid/issues). ## Authors * **Massimiliano Monaro** - [blog.friimaind.it](https://blog.friimaind.it) ## Contributing If you want to contribute to this project and make it better, your help is very welcome! :) ## App permissions The app could scan the QR Code of your Pi-hole to retrieve the token. This function uses the camera of your smartphone so this is the reason why the app needs the **camera permission**. ## License This project is licensed under the terms of the [GNU General Public License v2.0](LICENSE). ================================================ FILE: config.xml ================================================ Pi-hole Droid Monitor your Pi-hole with your Android smartphone Massimiliano Monaro ================================================ FILE: hooks/README.md ================================================ # Cordova Hooks Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide. ================================================ FILE: platforms/android/.gitignore ================================================ # Non-project-specific build files: build.xml local.properties /gradlew /gradlew.bat /gradle # Ant builds ant-build ant-gen # Eclipse builds gen out # Gradle builds /build ================================================ FILE: platforms/android/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/generated/source/buildConfig/debug/org/apache/cordova/BuildConfig.java ================================================ /** * Automatically generated file. DO NOT MODIFY */ package org.apache.cordova; public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "org.apache.cordova"; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = ""; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0"; } ================================================ FILE: platforms/android/CordovaLib/build/generated/source/buildConfig/release/org/apache/cordova/BuildConfig.java ================================================ /** * Automatically generated file. DO NOT MODIFY */ package org.apache.cordova; public final class BuildConfig { public static final boolean DEBUG = false; public static final String APPLICATION_ID = "org.apache.cordova"; public static final String BUILD_TYPE = "release"; public static final String FLAVOR = ""; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0"; } ================================================ FILE: platforms/android/CordovaLib/build/intermediates/bundles/debug/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/bundles/release/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeDebugAssets/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeDebugShaders/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeReleaseAssets/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/mergeReleaseShaders/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/packageDebugResources/compile-file-map.properties ================================================ #Sun Apr 23 15:43:32 CEST 2017 ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/packageDebugResources/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/packageReleaseResources/compile-file-map.properties ================================================ #Sun Apr 23 15:43:32 CEST 2017 ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental/packageReleaseResources/merger.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental-safeguard/debug/tag.txt ================================================ incremental task execution ================================================ FILE: platforms/android/CordovaLib/build/intermediates/incremental-safeguard/release/tag.txt ================================================ incremental task execution ================================================ FILE: platforms/android/CordovaLib/build/intermediates/manifests/aapt/debug/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build/intermediates/manifests/aapt/release/AndroidManifest.xml ================================================ ================================================ FILE: platforms/android/CordovaLib/build.gradle ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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. */ ext { apply from: 'cordova.gradle' cdvCompileSdkVersion = privateHelpers.getProjectTarget() cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools() } buildscript { repositories { mavenCentral() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' } } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' group = 'org.apache.cordova' version = '6.2.0' android { compileSdkVersion cdvCompileSdkVersion buildToolsVersion cdvBuildToolsVersion publishNonDefault true compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 targetCompatibility JavaVersion.VERSION_1_6 } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } } packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/NOTICE' } } install { repositories.mavenInstaller { pom { project { packaging 'aar' name 'Cordova' url 'https://cordova.apache.org' licenses { license { name 'The Apache Software License, Version 2.0' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { id 'stevengill' name 'Steve Gill' } } scm { connection 'https://git-wip-us.apache.org/repos/asf?p=cordova-android.git' developerConnection 'https://git-wip-us.apache.org/repos/asf?p=cordova-android.git' url 'https://git-wip-us.apache.org/repos/asf?p=cordova-android' } } } } } task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } artifacts { archives sourcesJar } bintray { user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_KEY') configurations = ['archives'] pkg { repo = 'maven' name = 'cordova-android' userOrg = 'cordova' licenses = ['Apache-2.0'] vcsUrl = 'https://git-wip-us.apache.org/repos/asf?p=cordova-android.git' websiteUrl = 'https://cordova.apache.org' issueTrackerUrl = 'https://issues.apache.org/jira/browse/CB' publicDownloadNumbers = true licenses = ['Apache-2.0'] labels = ['android', 'cordova', 'phonegap'] version { name = version released = new Date() vcsTag = version } } } ================================================ FILE: platforms/android/CordovaLib/cordova.gradle ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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. */ import java.util.regex.Pattern import groovy.swing.SwingBuilder String doEnsureValueExists(filePath, props, key) { if (props.get(key) == null) { throw new GradleException(filePath + ': Missing key required "' + key + '"') } return props.get(key) } String doGetProjectTarget() { def props = new Properties() file('project.properties').withReader { reader -> props.load(reader) } return doEnsureValueExists('project.properties', props, 'target') } String[] getAvailableBuildTools() { def buildToolsDir = new File(getAndroidSdkDir(), "build-tools") buildToolsDir.list() .findAll { it ==~ /[0-9.]+/ } .sort { a, b -> compareVersions(b, a) } } String doFindLatestInstalledBuildTools(String minBuildToolsVersion) { def availableBuildToolsVersions try { availableBuildToolsVersions = getAvailableBuildTools() } catch (e) { println "An exception occurred while trying to find the Android build tools." throw e } if (availableBuildToolsVersions.length > 0) { def highestBuildToolsVersion = availableBuildToolsVersions[0] if (compareVersions(highestBuildToolsVersion, minBuildToolsVersion) < 0) { throw new RuntimeException( "No usable Android build tools found. Highest installed version is " + highestBuildToolsVersion + "; minimum version required is " + minBuildToolsVersion + ".") } highestBuildToolsVersion } else { throw new RuntimeException( "No installed build tools found. Install the Android build tools version " + minBuildToolsVersion + " or higher.") } } // Return the first non-zero result of subtracting version list elements // pairwise. If they are all identical, return the difference in length of // the two lists. int compareVersionList(Collection aParts, Collection bParts) { def pairs = ([aParts, bParts]).transpose() pairs.findResult(aParts.size()-bParts.size()) {it[0] - it[1] != 0 ? it[0] - it[1] : null} } // Compare two version strings, such as "19.0.0" and "18.1.1.0". If all matched // elements are identical, the longer version is the largest by this method. // Examples: // "19.0.0" > "19" // "19.0.1" > "19.0.0" // "19.1.0" > "19.0.1" // "19" > "18.999.999" int compareVersions(String a, String b) { def aParts = a.tokenize('.').collect {it.toInteger()} def bParts = b.tokenize('.').collect {it.toInteger()} compareVersionList(aParts, bParts) } String getAndroidSdkDir() { def rootDir = project.rootDir def androidSdkDir = null String envVar = System.getenv("ANDROID_HOME") def localProperties = new File(rootDir, 'local.properties') String systemProperty = System.getProperty("android.home") if (envVar != null) { androidSdkDir = envVar } else if (localProperties.exists()) { Properties properties = new Properties() localProperties.withInputStream { instr -> properties.load(instr) } def sdkDirProp = properties.getProperty('sdk.dir') if (sdkDirProp != null) { androidSdkDir = sdkDirProp } else { sdkDirProp = properties.getProperty('android.dir') if (sdkDirProp != null) { androidSdkDir = (new File(rootDir, sdkDirProp)).getAbsolutePath() } } } if (androidSdkDir == null && systemProperty != null) { androidSdkDir = systemProperty } if (androidSdkDir == null) { throw new RuntimeException( "Unable to determine Android SDK directory.") } androidSdkDir } def doExtractIntFromManifest(name) { def manifestFile = file(android.sourceSets.main.manifest.srcFile) def pattern = Pattern.compile(name + "=\"(\\d+)\"") def matcher = pattern.matcher(manifestFile.getText()) matcher.find() return new BigInteger(matcher.group(1)) } def doExtractStringFromManifest(name) { def manifestFile = file(android.sourceSets.main.manifest.srcFile) def pattern = Pattern.compile(name + "=\"(\\S+)\"") def matcher = pattern.matcher(manifestFile.getText()) matcher.find() return matcher.group(1) } def doPromptForPassword(msg) { if (System.console() == null) { def ret = null new SwingBuilder().edt { dialog(modal: true, title: 'Enter password', alwaysOnTop: true, resizable: false, locationRelativeTo: null, pack: true, show: true) { vbox { label(text: msg) def input = passwordField() button(defaultButton: true, text: 'OK', actionPerformed: { ret = input.password; dispose(); }) } } } if (!ret) { throw new GradleException('User canceled build') } return new String(ret) } else { return System.console().readPassword('\n' + msg); } } def doGetConfigXml() { def xml = file("res/xml/config.xml").getText() // Disable namespace awareness since Cordova doesn't use them properly return new XmlParser(false, false).parseText(xml) } def doGetConfigPreference(name, defaultValue) { name = name.toLowerCase() def root = doGetConfigXml() def ret = defaultValue root.preference.each { it -> def attrName = it.attribute("name") if (attrName && attrName.toLowerCase() == name) { ret = it.attribute("value") } } return ret } // Properties exported here are visible to all plugins. ext { // These helpers are shared, but are not guaranteed to be stable / unchanged. privateHelpers = {} privateHelpers.getProjectTarget = { doGetProjectTarget() } privateHelpers.findLatestInstalledBuildTools = { doFindLatestInstalledBuildTools('19.1.0') } privateHelpers.extractIntFromManifest = { name -> doExtractIntFromManifest(name) } privateHelpers.extractStringFromManifest = { name -> doExtractStringFromManifest(name) } privateHelpers.promptForPassword = { msg -> doPromptForPassword(msg) } privateHelpers.ensureValueExists = { filePath, props, key -> doEnsureValueExists(filePath, props, key) } // These helpers can be used by plugins / projects and will not change. cdvHelpers = {} // Returns a XmlParser for the config.xml. Added in 4.1.0. cdvHelpers.getConfigXml = { doGetConfigXml() } // Returns the value for the desired . Added in 4.1.0. cdvHelpers.getConfigPreference = { name, defaultValue -> doGetConfigPreference(name, defaultValue) } } ================================================ FILE: platforms/android/CordovaLib/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. # Indicates whether an apk should be generated for each density. split.density=false # Project target. target=android-25 apk-configurations= renderscript.opt.level=O0 android.library=true ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; /** * The Class AuthenticationToken defines the userName and password to be used for authenticating a web resource */ public class AuthenticationToken { private String userName; private String password; /** * Gets the user name. * * @return the user name */ public String getUserName() { return userName; } /** * Sets the user name. * * @param userName * the new user name */ public void setUserName(String userName) { this.userName = userName; } /** * Gets the password. * * @return the password */ public String getPassword() { return password; } /** * Sets the password. * * @param password * the new password */ public void setPassword(String password) { this.password = password; } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import org.json.JSONArray; import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginResult; import org.json.JSONObject; public class CallbackContext { private static final String LOG_TAG = "CordovaPlugin"; private String callbackId; private CordovaWebView webView; protected boolean finished; private int changingThreads; public CallbackContext(String callbackId, CordovaWebView webView) { this.callbackId = callbackId; this.webView = webView; } public boolean isFinished() { return finished; } public boolean isChangingThreads() { return changingThreads > 0; } public String getCallbackId() { return callbackId; } public void sendPluginResult(PluginResult pluginResult) { synchronized (this) { if (finished) { LOG.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage()); return; } else { finished = !pluginResult.getKeepCallback(); } } webView.sendPluginResult(pluginResult, callbackId); } /** * Helper for success callbacks that just returns the Status.OK by default * * @param message The message to add to the success result. */ public void success(JSONObject message) { sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); } /** * Helper for success callbacks that just returns the Status.OK by default * * @param message The message to add to the success result. */ public void success(String message) { sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); } /** * Helper for success callbacks that just returns the Status.OK by default * * @param message The message to add to the success result. */ public void success(JSONArray message) { sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); } /** * Helper for success callbacks that just returns the Status.OK by default * * @param message The message to add to the success result. */ public void success(byte[] message) { sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); } /** * Helper for success callbacks that just returns the Status.OK by default * * @param message The message to add to the success result. */ public void success(int message) { sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); } /** * Helper for success callbacks that just returns the Status.OK by default */ public void success() { sendPluginResult(new PluginResult(PluginResult.Status.OK)); } /** * Helper for error callbacks that just returns the Status.ERROR by default * * @param message The message to add to the error result. */ public void error(JSONObject message) { sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); } /** * Helper for error callbacks that just returns the Status.ERROR by default * * @param message The message to add to the error result. */ public void error(String message) { sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); } /** * Helper for error callbacks that just returns the Status.ERROR by default * * @param message The message to add to the error result. */ public void error(int message) { sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import android.util.Pair; import android.util.SparseArray; /** * Provides a collection that maps unique request codes to CordovaPlugins and Integers. * Used to ensure that when plugins make requests for runtime permissions, those requests do not * collide with requests from other plugins that use the same request code value. */ public class CallbackMap { private int currentCallbackId = 0; private SparseArray> callbacks; public CallbackMap() { this.callbacks = new SparseArray>(); } /** * Stores a CordovaPlugin and request code and returns a new unique request code to use * in a permission request. * * @param receiver The plugin that is making the request * @param requestCode The original request code used by the plugin * @return A unique request code that can be used to retrieve this callback * with getAndRemoveCallback() */ public synchronized int registerCallback(CordovaPlugin receiver, int requestCode) { int mappedId = this.currentCallbackId++; callbacks.put(mappedId, new Pair(receiver, requestCode)); return mappedId; } /** * Retrieves and removes a callback stored in the map using the mapped request code * obtained from registerCallback() * * @param mappedId The request code obtained from registerCallback() * @return The CordovaPlugin and orignal request code that correspond to the * given mappedCode */ public synchronized Pair getAndRemoveCallback(int mappedId) { Pair callback = callbacks.get(mappedId); callbacks.remove(mappedId); return callback; } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/Config.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import java.util.List; import android.app.Activity; @Deprecated // Use Whitelist, CordovaPrefences, etc. directly. public class Config { private static final String TAG = "Config"; static ConfigXmlParser parser; private Config() { } public static void init(Activity action) { parser = new ConfigXmlParser(); parser.parse(action); //TODO: Add feature to bring this back. Some preferences should be overridden by intents, but not all parser.getPreferences().setPreferencesBundle(action.getIntent().getExtras()); } // Intended to be used for testing only; creates an empty configuration. public static void init() { if (parser == null) { parser = new ConfigXmlParser(); } } public static String getStartUrl() { if (parser == null) { return "file:///android_asset/www/index.html"; } return parser.getLaunchUrl(); } public static String getErrorUrl() { return parser.getPreferences().getString("errorurl", null); } public static List getPluginEntries() { return parser.getPluginEntries(); } public static CordovaPreferences getPreferences() { return parser.getPreferences(); } public static boolean isInitialized() { return parser != null; } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import java.io.IOException; import java.util.ArrayList; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; public class ConfigXmlParser { private static String TAG = "ConfigXmlParser"; private String launchUrl = "file:///android_asset/www/index.html"; private CordovaPreferences prefs = new CordovaPreferences(); private ArrayList pluginEntries = new ArrayList(20); public CordovaPreferences getPreferences() { return prefs; } public ArrayList getPluginEntries() { return pluginEntries; } public String getLaunchUrl() { return launchUrl; } public void parse(Context action) { // First checking the class namespace for config.xml int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName()); if (id == 0) { // If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml id = action.getResources().getIdentifier("config", "xml", action.getPackageName()); if (id == 0) { LOG.e(TAG, "res/xml/config.xml is missing!"); return; } } parse(action.getResources().getXml(id)); } boolean insideFeature = false; String service = "", pluginClass = "", paramType = ""; boolean onload = false; public void parse(XmlPullParser xml) { int eventType = -1; while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { handleStartTag(xml); } else if (eventType == XmlPullParser.END_TAG) { handleEndTag(xml); } try { eventType = xml.next(); } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } public void handleStartTag(XmlPullParser xml) { String strNode = xml.getName(); if (strNode.equals("feature")) { //Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc) //Set the bit for reading params insideFeature = true; service = xml.getAttributeValue(null, "name"); } else if (insideFeature && strNode.equals("param")) { paramType = xml.getAttributeValue(null, "name"); if (paramType.equals("service")) // check if it is using the older service param service = xml.getAttributeValue(null, "value"); else if (paramType.equals("package") || paramType.equals("android-package")) pluginClass = xml.getAttributeValue(null,"value"); else if (paramType.equals("onload")) onload = "true".equals(xml.getAttributeValue(null, "value")); } else if (strNode.equals("preference")) { String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH); String value = xml.getAttributeValue(null, "value"); prefs.set(name, value); } else if (strNode.equals("content")) { String src = xml.getAttributeValue(null, "src"); if (src != null) { setStartUrl(src); } } } public void handleEndTag(XmlPullParser xml) { String strNode = xml.getName(); if (strNode.equals("feature")) { pluginEntries.add(new PluginEntry(service, pluginClass, onload)); service = ""; pluginClass = ""; insideFeature = false; onload = false; } } private void setStartUrl(String src) { Pattern schemeRegex = Pattern.compile("^[a-z-]+://"); Matcher matcher = schemeRegex.matcher(src); if (matcher.find()) { launchUrl = src; } else { if (src.charAt(0) == '/') { src = src.substring(1); } launchUrl = "file:///android_asset/www/" + src; } } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import java.util.ArrayList; import java.util.Locale; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.annotation.SuppressLint; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.webkit.WebViewClient; import android.widget.FrameLayout; /** * This class is the main Android activity that represents the Cordova * application. It should be extended by the user to load the specific * html file that contains the application. * * As an example: * *
 *     package org.apache.cordova.examples;
 *
 *     import android.os.Bundle;
 *     import org.apache.cordova.*;
 *
 *     public class Example extends CordovaActivity {
 *       @Override
 *       public void onCreate(Bundle savedInstanceState) {
 *         super.onCreate(savedInstanceState);
 *         super.init();
 *         // Load your application
 *         loadUrl(launchUrl);
 *       }
 *     }
 * 
* * Cordova xml configuration: Cordova uses a configuration file at * res/xml/config.xml to specify its settings. See "The config.xml File" * guide in cordova-docs at http://cordova.apache.org/docs for the documentation * for the configuration. The use of the set*Property() methods is * deprecated in favor of the config.xml file. * */ public class CordovaActivity extends Activity { public static String TAG = "CordovaActivity"; // The webview for our app protected CordovaWebView appView; private static int ACTIVITY_STARTING = 0; private static int ACTIVITY_RUNNING = 1; private static int ACTIVITY_EXITING = 2; // Keep app running when pause is received. (default = true) // If true, then the JavaScript and native code continue to run in the background // when another application (activity) is started. protected boolean keepRunning = true; // Flag to keep immersive mode if set to fullscreen protected boolean immersiveMode; // Read from config.xml: protected CordovaPreferences preferences; protected String launchUrl; protected ArrayList pluginEntries; protected CordovaInterfaceImpl cordovaInterface; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { // need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception loadConfig(); String logLevel = preferences.getString("loglevel", "ERROR"); LOG.setLogLevel(logLevel); LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting"); LOG.d(TAG, "CordovaActivity.onCreate()"); if (!preferences.getBoolean("ShowTitle", false)) { getWindow().requestFeature(Window.FEATURE_NO_TITLE); } if (preferences.getBoolean("SetFullscreen", false)) { LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version."); preferences.set("Fullscreen", true); } if (preferences.getBoolean("Fullscreen", false)) { // NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen // (as was the case in previous cordova versions) if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) && !preferences.getBoolean("FullscreenNotImmersive", false)) { immersiveMode = true; } else { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } } else { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } super.onCreate(savedInstanceState); cordovaInterface = makeCordovaInterface(); if (savedInstanceState != null) { cordovaInterface.restoreInstanceState(savedInstanceState); } } protected void init() { appView = makeWebView(); createViews(); if (!appView.isInitialized()) { appView.init(cordovaInterface, pluginEntries, preferences); } cordovaInterface.onCordovaInit(appView.getPluginManager()); // Wire the hardware volume controls to control media if desired. String volumePref = preferences.getString("DefaultVolumeStream", ""); if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) { setVolumeControlStream(AudioManager.STREAM_MUSIC); } } @SuppressWarnings("deprecation") protected void loadConfig() { ConfigXmlParser parser = new ConfigXmlParser(); parser.parse(this); preferences = parser.getPreferences(); preferences.setPreferencesBundle(getIntent().getExtras()); launchUrl = parser.getLaunchUrl(); pluginEntries = parser.getPluginEntries(); Config.parser = parser; } //Suppressing warnings in AndroidStudio @SuppressWarnings({"deprecation", "ResourceType"}) protected void createViews() { //Why are we setting a constant as the ID? This should be investigated appView.getView().setId(100); appView.getView().setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(appView.getView()); if (preferences.contains("BackgroundColor")) { try { int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK); // Background of activity: appView.getView().setBackgroundColor(backgroundColor); } catch (NumberFormatException e){ e.printStackTrace(); } } appView.getView().requestFocusFromTouch(); } /** * Construct the default web view object. *

* Override this to customize the webview that is used. */ protected CordovaWebView makeWebView() { return new CordovaWebViewImpl(makeWebViewEngine()); } protected CordovaWebViewEngine makeWebViewEngine() { return CordovaWebViewImpl.createEngine(this, preferences); } protected CordovaInterfaceImpl makeCordovaInterface() { return new CordovaInterfaceImpl(this) { @Override public Object onMessage(String id, Object data) { // Plumb this to CordovaActivity.onMessage for backwards compatibility return CordovaActivity.this.onMessage(id, data); } }; } /** * Load the url into the webview. */ public void loadUrl(String url) { if (appView == null) { init(); } // If keepRunning this.keepRunning = preferences.getBoolean("KeepRunning", true); appView.loadUrlIntoView(url, true); } /** * Called when the system is about to start resuming a previous activity. */ @Override protected void onPause() { super.onPause(); LOG.d(TAG, "Paused the activity."); if (this.appView != null) { // CB-9382 If there is an activity that started for result and main activity is waiting for callback // result, we shoudn't stop WebView Javascript timers, as activity for result might be using them boolean keepRunning = this.keepRunning || this.cordovaInterface.activityResultCallback != null; this.appView.handlePause(keepRunning); } } /** * Called when the activity receives a new intent */ @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //Forward to plugins if (this.appView != null) this.appView.onNewIntent(intent); } /** * Called when the activity will start interacting with the user. */ @Override protected void onResume() { super.onResume(); LOG.d(TAG, "Resumed the activity."); if (this.appView == null) { return; } // Force window to have focus, so application always // receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least) this.getWindow().getDecorView().requestFocus(); this.appView.handleResume(this.keepRunning); } /** * Called when the activity is no longer visible to the user. */ @Override protected void onStop() { super.onStop(); LOG.d(TAG, "Stopped the activity."); if (this.appView == null) { return; } this.appView.handleStop(); } /** * Called when the activity is becoming visible to the user. */ @Override protected void onStart() { super.onStart(); LOG.d(TAG, "Started the activity."); if (this.appView == null) { return; } this.appView.handleStart(); } /** * The final call you receive before your activity is destroyed. */ @Override public void onDestroy() { LOG.d(TAG, "CordovaActivity.onDestroy()"); super.onDestroy(); if (this.appView != null) { appView.handleDestroy(); } } /** * Called when view focus is changed */ @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus && immersiveMode) { final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; getWindow().getDecorView().setSystemUiVisibility(uiOptions); } } @SuppressLint("NewApi") @Override public void startActivityForResult(Intent intent, int requestCode, Bundle options) { // Capture requestCode here so that it is captured in the setActivityResultCallback() case. cordovaInterface.setActivityResultRequestCode(requestCode); super.startActivityForResult(intent, requestCode, options); } /** * Called when an activity you launched exits, giving you the requestCode you started it with, * the resultCode it returned, and any additional data from it. * * @param requestCode The request code originally supplied to startActivityForResult(), * allowing you to identify who this result came from. * @param resultCode The integer result code returned by the child activity through its setResult(). * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { LOG.d(TAG, "Incoming Result. Request code = " + requestCode); super.onActivityResult(requestCode, resultCode, intent); cordovaInterface.onActivityResult(requestCode, resultCode, intent); } /** * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). * The errorCode parameter corresponds to one of the ERROR_* constants. * * @param errorCode The error code corresponding to an ERROR_* value. * @param description A String describing the error. * @param failingUrl The url that failed to load. */ public void onReceivedError(final int errorCode, final String description, final String failingUrl) { final CordovaActivity me = this; // If errorUrl specified, then load it final String errorUrl = preferences.getString("errorUrl", null); if ((errorUrl != null) && (!failingUrl.equals(errorUrl)) && (appView != null)) { // Load URL on UI thread me.runOnUiThread(new Runnable() { public void run() { me.appView.showWebPage(errorUrl, false, true, null); } }); } // If not, then display error dialog else { final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP); me.runOnUiThread(new Runnable() { public void run() { if (exit) { me.appView.getView().setVisibility(View.GONE); me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit); } } }); } } /** * Display an error dialog and optionally exit application. */ public void displayError(final String title, final String message, final String button, final boolean exit) { final CordovaActivity me = this; me.runOnUiThread(new Runnable() { public void run() { try { AlertDialog.Builder dlg = new AlertDialog.Builder(me); dlg.setMessage(message); dlg.setTitle(title); dlg.setCancelable(false); dlg.setPositiveButton(button, new AlertDialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); if (exit) { finish(); } } }); dlg.create(); dlg.show(); } catch (Exception e) { finish(); } } }); } /* * Hook in Cordova for menu plugins */ @Override public boolean onCreateOptionsMenu(Menu menu) { if (appView != null) { appView.getPluginManager().postMessage("onCreateOptionsMenu", menu); } return super.onCreateOptionsMenu(menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (appView != null) { appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (appView != null) { appView.getPluginManager().postMessage("onOptionsItemSelected", item); } return true; } /** * Called when a message is sent to plugin. * * @param id The message id * @param data The message data * @return Object or null */ public Object onMessage(String id, Object data) { if ("onReceivedError".equals(id)) { JSONObject d = (JSONObject) data; try { this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url")); } catch (JSONException e) { e.printStackTrace(); } } else if ("exit".equals(id)) { finish(); } return null; } protected void onSaveInstanceState(Bundle outState) { cordovaInterface.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } /** * Called by the system when the device configuration changes while your activity is running. * * @param newConfig The new device configuration */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (this.appView == null) { return; } PluginManager pm = this.appView.getPluginManager(); if (pm != null) { pm.onConfigurationChanged(newConfig); } } /** * Called by the system when the user grants permissions * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { try { cordovaInterface.onRequestPermissionResult(requestCode, permissions, grantResults); } catch (JSONException e) { LOG.d(TAG, "JSONException: Parameters fed into the method are not valid"); e.printStackTrace(); } } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.util.Base64; public class CordovaArgs { private JSONArray baseArgs; public CordovaArgs(JSONArray args) { this.baseArgs = args; } // Pass through the basics to the base args. public Object get(int index) throws JSONException { return baseArgs.get(index); } public boolean getBoolean(int index) throws JSONException { return baseArgs.getBoolean(index); } public double getDouble(int index) throws JSONException { return baseArgs.getDouble(index); } public int getInt(int index) throws JSONException { return baseArgs.getInt(index); } public JSONArray getJSONArray(int index) throws JSONException { return baseArgs.getJSONArray(index); } public JSONObject getJSONObject(int index) throws JSONException { return baseArgs.getJSONObject(index); } public long getLong(int index) throws JSONException { return baseArgs.getLong(index); } public String getString(int index) throws JSONException { return baseArgs.getString(index); } public Object opt(int index) { return baseArgs.opt(index); } public boolean optBoolean(int index) { return baseArgs.optBoolean(index); } public double optDouble(int index) { return baseArgs.optDouble(index); } public int optInt(int index) { return baseArgs.optInt(index); } public JSONArray optJSONArray(int index) { return baseArgs.optJSONArray(index); } public JSONObject optJSONObject(int index) { return baseArgs.optJSONObject(index); } public long optLong(int index) { return baseArgs.optLong(index); } public String optString(int index) { return baseArgs.optString(index); } public boolean isNull(int index) { return baseArgs.isNull(index); } // The interesting custom helpers. public byte[] getArrayBuffer(int index) throws JSONException { String encoded = baseArgs.getString(index); return Base64.decode(encoded, Base64.DEFAULT); } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import java.security.SecureRandom; import org.json.JSONArray; import org.json.JSONException; /** * Contains APIs that the JS can call. All functions in here should also have * an equivalent entry in CordovaChromeClient.java, and be added to * cordova-js/lib/android/plugin/android/promptbasednativeapi.js */ public class CordovaBridge { private static final String LOG_TAG = "CordovaBridge"; private PluginManager pluginManager; private NativeToJsMessageQueue jsMessageQueue; private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread. public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) { this.pluginManager = pluginManager; this.jsMessageQueue = jsMessageQueue; } public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { if (!verifySecret("exec()", bridgeSecret)) { return null; } // If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666. // We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string. if (arguments == null) { return "@Null arguments."; } jsMessageQueue.setPaused(true); try { // Tell the resourceApi what thread the JS is running on. CordovaResourceApi.jsThread = Thread.currentThread(); pluginManager.exec(service, action, callbackId, arguments); String ret = null; if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) { ret = jsMessageQueue.popAndEncode(false); } return ret; } catch (Throwable e) { e.printStackTrace(); return ""; } finally { jsMessageQueue.setPaused(false); } } public void jsSetNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { if (!verifySecret("setNativeToJsBridgeMode()", bridgeSecret)) { return; } jsMessageQueue.setBridgeMode(value); } public String jsRetrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { if (!verifySecret("retrieveJsMessages()", bridgeSecret)) { return null; } return jsMessageQueue.popAndEncode(fromOnlineEvent); } private boolean verifySecret(String action, int bridgeSecret) throws IllegalAccessException { if (!jsMessageQueue.isBridgeEnabled()) { if (bridgeSecret == -1) { LOG.d(LOG_TAG, action + " call made before bridge was enabled."); } else { LOG.d(LOG_TAG, "Ignoring " + action + " from previous page load."); } return false; } // Bridge secret wrong and bridge not due to it being from the previous page. if (expectedBridgeSecret < 0 || bridgeSecret != expectedBridgeSecret) { LOG.e(LOG_TAG, "Bridge access attempt with wrong secret token, possibly from malicious code. Disabling exec() bridge!"); clearBridgeSecret(); throw new IllegalAccessException(); } return true; } /** Called on page transitions */ void clearBridgeSecret() { expectedBridgeSecret = -1; } public boolean isSecretEstablished() { return expectedBridgeSecret != -1; } /** Called by cordova.js to initialize the bridge. */ int generateBridgeSecret() { SecureRandom randGen = new SecureRandom(); expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE); return expectedBridgeSecret; } public void reset() { jsMessageQueue.reset(); clearBridgeSecret(); } public String promptOnJsPrompt(String origin, String message, String defaultValue) { if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) { JSONArray array; try { array = new JSONArray(defaultValue.substring(4)); int bridgeSecret = array.getInt(0); String service = array.getString(1); String action = array.getString(2); String callbackId = array.getString(3); String r = jsExec(bridgeSecret, service, action, callbackId, message); return r == null ? "" : r; } catch (JSONException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return ""; } // Sets the native->JS bridge mode. else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) { try { int bridgeSecret = Integer.parseInt(defaultValue.substring(16)); jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message)); } catch (NumberFormatException e){ e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return ""; } // Polling for JavaScript messages else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) { int bridgeSecret = Integer.parseInt(defaultValue.substring(9)); try { String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message)); return r == null ? "" : r; } catch (IllegalAccessException e) { e.printStackTrace(); } return ""; } else if (defaultValue != null && defaultValue.startsWith("gap_init:")) { // Protect against random iframes being able to talk through the bridge. // Trust only pages which the app would have been allowed to navigate to anyway. if (pluginManager.shouldAllowBridgeAccess(origin)) { // Enable the bridge int bridgeMode = Integer.parseInt(defaultValue.substring(9)); jsMessageQueue.setBridgeMode(bridgeMode); // Tell JS the bridge secret. int secret = generateBridgeSecret(); return ""+secret; } else { LOG.e(LOG_TAG, "gap_init called from restricted origin: " + origin); } return ""; } return null; } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.X509Certificate; import android.webkit.ClientCertRequest; /** * Implementation of the ICordovaClientCertRequest for Android WebView. */ public class CordovaClientCertRequest implements ICordovaClientCertRequest { private final ClientCertRequest request; public CordovaClientCertRequest(ClientCertRequest request) { this.request = request; } /** * Cancel this request */ public void cancel() { request.cancel(); } /* * Returns the host name of the server requesting the certificate. */ public String getHost() { return request.getHost(); } /* * Returns the acceptable types of asymmetric keys (can be null). */ public String[] getKeyTypes() { return request.getKeyTypes(); } /* * Returns the port number of the server requesting the certificate. */ public int getPort() { return request.getPort(); } /* * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). */ public Principal[] getPrincipals() { return request.getPrincipals(); } /* * Ignore the request for now. Do not remember user's choice. */ public void ignore() { request.ignore(); } /* * Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests. * * @param privateKey The privateKey * @param chain The certificate chain */ public void proceed(PrivateKey privateKey, X509Certificate[] chain) { request.proceed(privateKey, chain); } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.view.KeyEvent; import android.widget.EditText; /** * Helper class for WebViews to implement prompt(), alert(), confirm() dialogs. */ public class CordovaDialogsHelper { private final Context context; private AlertDialog lastHandledDialog; public CordovaDialogsHelper(Context context) { this.context = context; } public void showAlert(String message, final Result result) { AlertDialog.Builder dlg = new AlertDialog.Builder(context); dlg.setMessage(message); dlg.setTitle("Alert"); //Don't let alerts break the back button dlg.setCancelable(true); dlg.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { result.gotResult(true, null); } }); dlg.setOnCancelListener( new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { result.gotResult(false, null); } }); dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { //DO NOTHING public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { result.gotResult(true, null); return false; } else return true; } }); lastHandledDialog = dlg.show(); } public void showConfirm(String message, final Result result) { AlertDialog.Builder dlg = new AlertDialog.Builder(context); dlg.setMessage(message); dlg.setTitle("Confirm"); dlg.setCancelable(true); dlg.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { result.gotResult(true, null); } }); dlg.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { result.gotResult(false, null); } }); dlg.setOnCancelListener( new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { result.gotResult(false, null); } }); dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { //DO NOTHING public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { result.gotResult(false, null); return false; } else return true; } }); lastHandledDialog = dlg.show(); } /** * Tell the client to display a prompt dialog to the user. * If the client returns true, WebView will assume that the client will * handle the prompt dialog and call the appropriate JsPromptResult method. * * Since we are hacking prompts for our own purposes, we should not be using them for * this purpose, perhaps we should hack console.log to do this instead! */ public void showPrompt(String message, String defaultValue, final Result result) { // Returning false would also show a dialog, but the default one shows the origin (ugly). AlertDialog.Builder dlg = new AlertDialog.Builder(context); dlg.setMessage(message); final EditText input = new EditText(context); if (defaultValue != null) { input.setText(defaultValue); } dlg.setView(input); dlg.setCancelable(false); dlg.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { String userText = input.getText().toString(); result.gotResult(true, userText); } }); dlg.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { result.gotResult(false, null); } }); lastHandledDialog = dlg.show(); } public void destroyLastDialog(){ if (lastHandledDialog != null){ lastHandledDialog.cancel(); } } public interface Result { public void gotResult(boolean success, String value); } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import android.webkit.HttpAuthHandler; /** * Specifies interface for HTTP auth handler object which is used to handle auth requests and * specifying user credentials. */ public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler { private final HttpAuthHandler handler; public CordovaHttpAuthHandler(HttpAuthHandler handler) { this.handler = handler; } /** * Instructs the WebView to cancel the authentication request. */ public void cancel () { this.handler.cancel(); } /** * Instructs the WebView to proceed with the authentication with the given credentials. * * @param username * @param password */ public void proceed (String username, String password) { this.handler.proceed(username, password); } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import android.app.Activity; import android.content.Intent; import org.apache.cordova.CordovaPlugin; import java.util.concurrent.ExecutorService; /** * The Activity interface that is implemented by CordovaActivity. * It is used to isolate plugin development, and remove dependency on entire Cordova library. */ public interface CordovaInterface { /** * Launch an activity for which you would like a result when it finished. When this activity exits, * your onActivityResult() method will be called. * * @param command The command object * @param intent The intent to start * @param requestCode The request code that is passed to callback to identify the activity */ abstract public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode); /** * Set the plugin to be called when a sub-activity exits. * * @param plugin The plugin on which onActivityResult is to be called */ abstract public void setActivityResultCallback(CordovaPlugin plugin); /** * Get the Android activity. * * @return the Activity */ public abstract Activity getActivity(); /** * Called when a message is sent to plugin. * * @param id The message id * @param data The message data * @return Object or null */ public Object onMessage(String id, Object data); /** * Returns a shared thread pool that can be used for background tasks. */ public ExecutorService getThreadPool(); /** * Sends a permission request to the activity for one permission. */ public void requestPermission(CordovaPlugin plugin, int requestCode, String permission); /** * Sends a permission request to the activity for a group of permissions */ public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions); /** * Check for a permission. Returns true if the permission is granted, false otherwise. */ public boolean hasPermission(String permission); } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.util.Pair; import org.json.JSONException; import org.json.JSONObject; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Default implementation of CordovaInterface. */ public class CordovaInterfaceImpl implements CordovaInterface { private static final String TAG = "CordovaInterfaceImpl"; protected Activity activity; protected ExecutorService threadPool; protected PluginManager pluginManager; protected ActivityResultHolder savedResult; protected CallbackMap permissionResultCallbacks; protected CordovaPlugin activityResultCallback; protected String initCallbackService; protected int activityResultRequestCode; protected boolean activityWasDestroyed = false; protected Bundle savedPluginState; public CordovaInterfaceImpl(Activity activity) { this(activity, Executors.newCachedThreadPool()); } public CordovaInterfaceImpl(Activity activity, ExecutorService threadPool) { this.activity = activity; this.threadPool = threadPool; this.permissionResultCallbacks = new CallbackMap(); } @Override public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) { setActivityResultCallback(command); try { activity.startActivityForResult(intent, requestCode); } catch (RuntimeException e) { // E.g.: ActivityNotFoundException activityResultCallback = null; throw e; } } @Override public void setActivityResultCallback(CordovaPlugin plugin) { // Cancel any previously pending activity. if (activityResultCallback != null) { activityResultCallback.onActivityResult(activityResultRequestCode, Activity.RESULT_CANCELED, null); } activityResultCallback = plugin; } @Override public Activity getActivity() { return activity; } @Override public Object onMessage(String id, Object data) { if ("exit".equals(id)) { activity.finish(); } return null; } @Override public ExecutorService getThreadPool() { return threadPool; } /** * Dispatches any pending onActivityResult callbacks and sends the resume event if the * Activity was destroyed by the OS. */ public void onCordovaInit(PluginManager pluginManager) { this.pluginManager = pluginManager; if (savedResult != null) { onActivityResult(savedResult.requestCode, savedResult.resultCode, savedResult.intent); } else if(activityWasDestroyed) { // If there was no Activity result, we still need to send out the resume event if the // Activity was destroyed by the OS activityWasDestroyed = false; if(pluginManager != null) { CoreAndroid appPlugin = (CoreAndroid) pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME); if(appPlugin != null) { JSONObject obj = new JSONObject(); try { obj.put("action", "resume"); } catch (JSONException e) { LOG.e(TAG, "Failed to create event message", e); } appPlugin.sendResumeEvent(new PluginResult(PluginResult.Status.OK, obj)); } } } } /** * Routes the result to the awaiting plugin. Returns false if no plugin was waiting. */ public boolean onActivityResult(int requestCode, int resultCode, Intent intent) { CordovaPlugin callback = activityResultCallback; if(callback == null && initCallbackService != null) { // The application was restarted, but had defined an initial callback // before being shut down. savedResult = new ActivityResultHolder(requestCode, resultCode, intent); if (pluginManager != null) { callback = pluginManager.getPlugin(initCallbackService); if(callback != null) { callback.onRestoreStateForActivityResult(savedPluginState.getBundle(callback.getServiceName()), new ResumeCallback(callback.getServiceName(), pluginManager)); } } } activityResultCallback = null; if (callback != null) { LOG.d(TAG, "Sending activity result to plugin"); initCallbackService = null; savedResult = null; callback.onActivityResult(requestCode, resultCode, intent); return true; } LOG.w(TAG, "Got an activity result, but no plugin was registered to receive it" + (savedResult != null ? " yet!" : ".")); return false; } /** * Call this from your startActivityForResult() overload. This is required to catch the case * where plugins use Activity.startActivityForResult() + CordovaInterface.setActivityResultCallback() * rather than CordovaInterface.startActivityForResult(). */ public void setActivityResultRequestCode(int requestCode) { activityResultRequestCode = requestCode; } /** * Saves parameters for startActivityForResult(). */ public void onSaveInstanceState(Bundle outState) { if (activityResultCallback != null) { String serviceName = activityResultCallback.getServiceName(); outState.putString("callbackService", serviceName); } if(pluginManager != null){ outState.putBundle("plugin", pluginManager.onSaveInstanceState()); } } /** * Call this from onCreate() so that any saved startActivityForResult parameters will be restored. */ public void restoreInstanceState(Bundle savedInstanceState) { initCallbackService = savedInstanceState.getString("callbackService"); savedPluginState = savedInstanceState.getBundle("plugin"); activityWasDestroyed = true; } private static class ActivityResultHolder { private int requestCode; private int resultCode; private Intent intent; public ActivityResultHolder(int requestCode, int resultCode, Intent intent) { this.requestCode = requestCode; this.resultCode = resultCode; this.intent = intent; } } /** * Called by the system when the user grants permissions * * @param requestCode * @param permissions * @param grantResults */ public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException { Pair callback = permissionResultCallbacks.getAndRemoveCallback(requestCode); if(callback != null) { callback.first.onRequestPermissionResult(callback.second, permissions, grantResults); } } public void requestPermission(CordovaPlugin plugin, int requestCode, String permission) { String[] permissions = new String [1]; permissions[0] = permission; requestPermissions(plugin, requestCode, permissions); } public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) { int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode); getActivity().requestPermissions(permissions, mappedRequestCode); } public boolean hasPermission(String permission) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { int result = activity.checkSelfPermission(permission); return PackageManager.PERMISSION_GRANTED == result; } else { return true; } } } ================================================ FILE: platforms/android/CordovaLib/src/org/apache/cordova/CordovaPlugin.java ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 org.apache.cordova; import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaWebView; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CallbackContext; import org.json.JSONArray; import org.json.JSONException; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.net.Uri; import android.os.Build; import android.os.Bundle; import java.io.FileNotFoundException; import java.io.IOException; /** * Plugins must extend this class and override one of the execute methods. */ public class CordovaPlugin { public CordovaWebView webView; public CordovaInterface cordova; protected CordovaPreferences preferences; private String serviceName; /** * Call this after constructing to initialize the plugin. * Final because we want to be able to change args without breaking plugins. */ public final void privateInitialize(String serviceName, CordovaInterface cordova, CordovaWebView webView, CordovaPreferences preferences) { assert this.cordova == null; this.serviceName = serviceName; this.cordova = cordova; this.webView = webView; this.preferences = preferences; initialize(cordova, webView); pluginInitialize(); } /** * Called after plugin construction and fields have been initialized. * Prefer to use pluginInitialize instead since there is no value in * having parameters on the initialize() function. */ public void initialize(CordovaInterface cordova, CordovaWebView webView) { } /** * Called after plugin construction and fields have been initialized. */ protected void pluginInitialize() { } /** * Returns the plugin's service name (what you'd use when calling pluginManger.getPlugin()) */ public String getServiceName() { return serviceName; } /** * Executes the request. * * This method is called from the WebView thread. To do a non-trivial amount of work, use: * cordova.getThreadPool().execute(runnable); * * To run on the UI thread, use: * cordova.getActivity().runOnUiThread(runnable); * * @param action The action to execute. * @param rawArgs The exec() arguments in JSON form. * @param callbackContext The callback context used when calling back into JavaScript. * @return Whether the action was valid. */ public boolean execute(String action, String rawArgs, CallbackContext callbackContext) throws JSONException { JSONArray args = new JSONArray(rawArgs); return execute(action, args, callbackContext); } /** * Executes the request. * * This method is called from the WebView thread. To do a non-trivial amount of work, use: * cordova.getThreadPool().execute(runnable); * * To run on the UI thread, use: * cordova.getActivity().runOnUiThread(runnable); * * @param action The action to execute. * @param args The exec() arguments. * @param callbackContext The callback context used when calling back into JavaScript. * @return Whether the action was valid. */ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { CordovaArgs cordovaArgs = new CordovaArgs(args); return execute(action, cordovaArgs, callbackContext); } /** * Executes the request. * * This method is called from the WebView thread. To do a non-trivial amount of work, use: * cordova.getThreadPool().execute(runnable); * * To run on the UI thread, use: * cordova.getActivity().runOnUiThread(runnable); * * @param action The action to execute. * @param args The exec() arguments, wrapped with some Cordova helpers. * @param callbackContext The callback context used when calling back into JavaScript. * @return Whether the action was valid. */ public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { return false; } /** * Called when the system is about to start resuming a previous activity. * * @param multitasking Flag indicating if multitasking is turned on for app */ public void onPause(boolean multitasking) { } /** * Called when the activity will start interacting with the user. * * @param multitasking Flag indicating if multitasking is turned on for app */ public void onResume(boolean multitasking) { } /** * Called when the activity is becoming visible to the user. */ public void onStart() { } /** * Called when the activity is no longer visible to the user. */ public void onStop() { } /** * Called when the activity receives a new intent. */ public void onNewIntent(Intent intent) { } /** * The final call you receive before your activity is destroyed. */ public void onDestroy() { } /** * Called when the Activity is being destroyed (e.g. if a plugin calls out to an external * Activity and the OS kills the CordovaActivity in the background). The plugin should save its * state in this method only if it is awaiting the result of an external Activity and needs * to preserve some information so as to handle that result; onRestoreStateForActivityResult() * will only be called if the plugin is the recipient of an Activity result * * @return Bundle containing the state of the plugin or null if state does not need to be saved */ public Bundle onSaveInstanceState() { return null; } /** * Called when a plugin is the recipient of an Activity result after the CordovaActivity has * been destroyed. The Bundle will be the same as the one the plugin returned in * onSaveInstanceState() * * @param state Bundle containing the state of the plugin * @param callbackContext Replacement Context to return the plugin result to */ public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {} /** * Called when a message is sent to plugin. * * @param id The message id * @param data The message data * @return Object to stop propagation or null */ public Object onMessage(String id, Object data) { return null; } /** * Called when an activity you launched exits, giving you the requestCode you started it with, * the resultCode it returned, and any additional data from it. * * @param requestCode The request code originally supplied to startActivityForResult(), * allowing you to identify who this result came from. * @param resultCode The integer result code returned by the child activity through its setResult(). * @param intent An Intent, which can return result data to the caller (various data can be * attached to Intent "extras"). */ public void onActivityResult(int requestCode, int resultCode, Intent intent) { } /** * Hook for blocking the loading of external resources. * * This will be called when the WebView's shouldInterceptRequest wants to * know whether to open a connection to an external resource. Return false * to block the request: if any plugin returns false, Cordova will block * the request. If all plugins return null, the default policy will be * enforced. If at least one plugin returns true, and no plugins return * false, then the request will proceed. * * Note that this only affects resource requests which are routed through * WebViewClient.shouldInterceptRequest, such as XMLHttpRequest requests and * img tag loads. WebSockets and media requests (such as

  • ', { 'class': classes.sPageButton+' '+btnClass, 'id': idx === 0 && typeof button === 'string' ? settings.sTableId +'_'+ button : null } ) .append( $('', { 'href': '#', 'aria-controls': settings.sTableId, 'aria-label': aria[ button ], 'data-dt-idx': counter, 'tabindex': settings.iTabIndex } ) .html( btnDisplay ) ) .appendTo( container ); settings.oApi._fnBindAction( node, {action: button}, clickHandler ); counter++; } } } }; // IE9 throws an 'unknown error' if document.activeElement is used // inside an iframe or frame. var activeEl; try { // Because this approach is destroying and recreating the paging // elements, focus is lost on the select button which is bad for // accessibility. So we want to restore focus once the draw has // completed activeEl = $(host).find(document.activeElement).data('dt-idx'); } catch (e) {} attach( $(host).empty().html('